├── .bandit.yml ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── 01_bug.yml │ ├── 02_feature.yml │ └── config.yml ├── SECURITY.md └── workflows │ └── main.yml ├── .gitignore ├── .mailmap ├── .pre-commit-config.yaml ├── .pre-commit-hooks.yaml ├── .pylintrc ├── .readthedocs.yaml ├── CONTRIBUTING.rst ├── CONTRIBUTORS.txt ├── LICENSE ├── README.rst ├── bin └── gen-pycodestyle-plugin ├── dev-requirements.txt ├── docs ├── build │ └── .keep └── source │ ├── conf.py │ ├── faq.rst │ ├── glossary.rst │ ├── index.rst │ ├── internal │ ├── .keep │ ├── checker.rst │ ├── cli.rst │ ├── contributing.rst │ ├── formatters.rst │ ├── index.rst │ ├── option_handling.rst │ ├── plugin_handling.rst │ ├── releases.rst │ ├── start-to-finish.rst │ ├── utils.rst │ ├── writing-code.rst │ └── writing-documentation.rst │ ├── plugin-development │ ├── .keep │ ├── formatters.rst │ ├── index.rst │ ├── plugin-parameters.rst │ └── registering-plugins.rst │ ├── release-notes │ ├── 0.6.0.rst │ ├── 0.7.0.rst │ ├── 0.8.0.rst │ ├── 0.9.0.rst │ ├── 1.0.0.rst │ ├── 1.1.0.rst │ ├── 1.2.0.rst │ ├── 1.3.0.rst │ ├── 1.3.1.rst │ ├── 1.4.0.rst │ ├── 1.5.0.rst │ ├── 1.6.0.rst │ ├── 1.6.1.rst │ ├── 1.6.2.rst │ ├── 1.7.0.rst │ ├── 2.0.0.rst │ ├── 2.1.0.rst │ ├── 2.2.0.rst │ ├── 2.2.1.rst │ ├── 2.2.2.rst │ ├── 2.2.3.rst │ ├── 2.2.4.rst │ ├── 2.2.5.rst │ ├── 2.3.0.rst │ ├── 2.4.0.rst │ ├── 2.4.1.rst │ ├── 2.5.0.rst │ ├── 2.5.1.rst │ ├── 2.5.2.rst │ ├── 2.5.3.rst │ ├── 2.5.4.rst │ ├── 2.5.5.rst │ ├── 2.6.0.rst │ ├── 2.6.1.rst │ ├── 2.6.2.rst │ ├── 3.0.0.rst │ ├── 3.0.1.rst │ ├── 3.0.2.rst │ ├── 3.0.3.rst │ ├── 3.0.4.rst │ ├── 3.1.0.rst │ ├── 3.1.1.rst │ ├── 3.2.0.rst │ ├── 3.2.1.rst │ ├── 3.3.0.rst │ ├── 3.4.0.rst │ ├── 3.4.1.rst │ ├── 3.5.0.rst │ ├── 3.6.0.rst │ ├── 3.7.0.rst │ ├── 3.7.1.rst │ ├── 3.7.2.rst │ ├── 3.7.3.rst │ ├── 3.7.4.rst │ ├── 3.7.5.rst │ ├── 3.7.6.rst │ ├── 3.7.7.rst │ ├── 3.7.8.rst │ ├── 3.7.9.rst │ ├── 3.8.0.rst │ ├── 3.8.1.rst │ ├── 3.8.2.rst │ ├── 3.8.3.rst │ ├── 3.8.4.rst │ ├── 3.9.0.rst │ ├── 3.9.1.rst │ ├── 3.9.2.rst │ ├── 4.0.0.rst │ ├── 4.0.1.rst │ ├── 5.0.0.rst │ ├── 5.0.1.rst │ ├── 5.0.2.rst │ ├── 5.0.3.rst │ ├── 5.0.4.rst │ ├── 6.0.0.rst │ ├── 6.1.0.rst │ ├── 7.0.0.rst │ ├── 7.1.0.rst │ ├── 7.1.1.rst │ ├── 7.1.2.rst │ ├── 7.2.0.rst │ └── index.rst │ ├── requirements.txt │ └── user │ ├── .keep │ ├── configuration.rst │ ├── error-codes.rst │ ├── index.rst │ ├── invocation.rst │ ├── options.rst │ ├── python-api.rst │ ├── using-hooks.rst │ ├── using-plugins.rst │ └── violations.rst ├── example-plugin ├── setup.py └── src │ └── flake8_example_plugin │ ├── __init__.py │ ├── off_by_default.py │ └── on_by_default.py ├── pytest.ini ├── setup.cfg ├── setup.py ├── src └── flake8 │ ├── __init__.py │ ├── __main__.py │ ├── _compat.py │ ├── api │ ├── __init__.py │ └── legacy.py │ ├── checker.py │ ├── defaults.py │ ├── discover_files.py │ ├── exceptions.py │ ├── formatting │ ├── __init__.py │ ├── _windows_color.py │ ├── base.py │ └── default.py │ ├── main │ ├── __init__.py │ ├── application.py │ ├── cli.py │ ├── debug.py │ └── options.py │ ├── options │ ├── __init__.py │ ├── aggregator.py │ ├── config.py │ ├── manager.py │ └── parse_args.py │ ├── plugins │ ├── __init__.py │ ├── finder.py │ ├── pycodestyle.py │ ├── pyflakes.py │ └── reporter.py │ ├── processor.py │ ├── statistics.py │ ├── style_guide.py │ ├── utils.py │ └── violation.py ├── tests ├── __init__.py ├── conftest.py ├── integration │ ├── __init__.py │ ├── subdir │ │ ├── __init__.py │ │ └── aplugin.py │ ├── test_aggregator.py │ ├── test_api_legacy.py │ ├── test_checker.py │ ├── test_main.py │ └── test_plugins.py └── unit │ ├── __init__.py │ ├── conftest.py │ ├── plugins │ ├── __init__.py │ ├── finder_test.py │ ├── pycodestyle_test.py │ └── reporter_test.py │ ├── test_application.py │ ├── test_base_formatter.py │ ├── test_checker_manager.py │ ├── test_debug.py │ ├── test_decision_engine.py │ ├── test_defaults.py │ ├── test_discover_files.py │ ├── test_exceptions.py │ ├── test_file_checker.py │ ├── test_file_processor.py │ ├── test_filenameonly_formatter.py │ ├── test_legacy_api.py │ ├── test_main_options.py │ ├── test_nothing_formatter.py │ ├── test_option.py │ ├── test_option_manager.py │ ├── test_options_config.py │ ├── test_pyflakes_codes.py │ ├── test_statistics.py │ ├── test_style_guide.py │ ├── test_utils.py │ └── test_violation.py └── tox.ini /.bandit.yml: -------------------------------------------------------------------------------- 1 | skips: 2 | - B101 # Ignore defensive `assert`s (especially useful for mypy) 3 | - B404 # Ignore warnings about importing subprocess 4 | - B603 # Ignore warnings about calling subprocess.Popen without shell=True 5 | - B607 # Ignore warnings about calling subprocess.Popen without a full path to executable 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: asottile 2 | tidelift: pypi/flake8 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/01_bug.yml: -------------------------------------------------------------------------------- 1 | name: bug report 2 | description: something went wrong 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: > 7 | Please read this brief portion of documentation before going any 8 | further: 9 | https://flake8.pycqa.org/en/latest/internal/contributing.html#filing-a-bug 10 | 11 | - type: markdown 12 | attributes: 13 | value: > 14 | **NOTE: flake8 is a linting framework and does not implement any 15 | checks** 16 | 17 | - type: markdown 18 | attributes: 19 | value: > 20 | _if you are reporting a problem with a particular check, please track 21 | down the plugin which implements that check_ 22 | 23 | - type: textarea 24 | id: install 25 | attributes: 26 | label: how did you install flake8? 27 | description: 'note: this will be rendered as ```console automatically' 28 | placeholder: | 29 | $ pip install flake8 # or `brew install flake8` etc. 30 | Collecting flake8 31 | ... 32 | Successfully installed flake8... 33 | render: console 34 | validations: 35 | required: true 36 | 37 | - type: markdown 38 | attributes: 39 | value: > 40 | **Note**: Some *nix distributions patch Flake8 arbitrarily to 41 | accommodate incompatible software versions. If you're on one of those 42 | distributions, your issue may be closed and you will be asked to open 43 | an issue with your distribution package maintainers instead. 44 | 45 | - type: textarea 46 | id: bug-report 47 | attributes: 48 | label: unmodified output of `flake8 --bug-report` 49 | description: 'note: this will be rendered as ```json automatically' 50 | placeholder: | 51 | { 52 | "platform": { 53 | "...": "... 54 | } 55 | } 56 | render: json 57 | validations: 58 | required: true 59 | 60 | - type: textarea 61 | id: freeform 62 | attributes: 63 | label: describe the problem 64 | description: > 65 | please provide **sample code** and **directions for reproducing 66 | your problem** including the **commands you ran**, their 67 | **unedited output**, and **what you expected to happen** 68 | value: | 69 | #### what I expected to happen 70 | 71 | ... 72 | 73 | #### sample code 74 | 75 | ```python 76 | print('hello world!') 77 | ``` 78 | 79 | #### commands ran 80 | 81 | ```console 82 | $ flake8 t.py 83 | ... 84 | ``` 85 | validations: 86 | required: true 87 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02_feature.yml: -------------------------------------------------------------------------------- 1 | name: feature request 2 | description: a new feature! 3 | body: 4 | - type: markdown 5 | attributes: 6 | value: > 7 | Please read this brief portion of documentation before going any 8 | further: 9 | https://flake8.pycqa.org/en/latest/internal/contributing.html#filing-a-bug 10 | 11 | - type: markdown 12 | attributes: 13 | value: '**NOTE: flake8 is a linting framework and does not implement any checks**' 14 | 15 | - type: markdown 16 | attributes: 17 | value: '**NOTE: if you ask about `pyproject.toml` your issue will be closed as a duplicate of [#234](https://github.com/PyCQA/flake8/issues/234)**' 18 | 19 | - type: textarea 20 | id: freeform 21 | attributes: 22 | label: describe the request 23 | description: > 24 | please describe your use case and why the current feature set does 25 | not satisfy your needs 26 | validations: 27 | required: true 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: problem with E___ or W___ codes 4 | url: https://github.com/PyCQA/pycodestyle/issues 5 | about: flake8 does not implement any checks, perhaps you want pycodestyle? 6 | - name: problem with F___ codes 7 | url: https://github.com/PyCQA/pyflakes/issues 8 | about: flake8 does not implement any checks, perhaps you want pyflakes? 9 | - name: problem with C___ codes 10 | url: https://github.com/PyCQA/mccabe/issues 11 | about: flake8 does not implement any checks, perhaps you want mccabe? 12 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | ## security contact information 2 | 3 | to report a security vulnerability, please use the 4 | [Tidelift security contact](https://tidelift.com/security). 5 | Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | tags: 7 | pull_request: 8 | 9 | jobs: 10 | main: 11 | strategy: 12 | matrix: 13 | include: 14 | # linux 15 | - os: ubuntu-latest 16 | python: pypy-3.9 17 | toxenv: py 18 | - os: ubuntu-latest 19 | python: 3.9 20 | toxenv: py 21 | - os: ubuntu-latest 22 | python: '3.10' 23 | toxenv: py 24 | - os: ubuntu-latest 25 | python: '3.11' 26 | toxenv: py 27 | - os: ubuntu-latest 28 | python: '3.12' 29 | toxenv: py 30 | - os: ubuntu-latest 31 | python: '3.13' 32 | toxenv: py 33 | # windows 34 | - os: windows-latest 35 | python: 3.9 36 | toxenv: py 37 | # misc 38 | - os: ubuntu-latest 39 | python: '3.10' 40 | toxenv: docs 41 | - os: ubuntu-latest 42 | python: '3.10' 43 | toxenv: linters 44 | - os: ubuntu-latest 45 | python: '3.10' 46 | toxenv: dogfood 47 | runs-on: ${{ matrix.os }} 48 | steps: 49 | - uses: actions/checkout@v2 50 | - uses: actions/setup-python@v2 51 | with: 52 | python-version: ${{ matrix.python }} 53 | - run: python -mpip install --upgrade setuptools pip tox virtualenv 54 | - run: tox -e ${{ matrix.toxenv }} 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg 2 | *.egg-info 3 | *.log 4 | *.pyc 5 | *.sw* 6 | *.zip 7 | .cache 8 | .coverage 9 | .coverage.* 10 | .eggs 11 | .tox 12 | /.mypy_cache 13 | build 14 | dist 15 | docs/build/html/* 16 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Ian Stapleton Cordasco Ian Cordasco 2 | Ian Stapleton Cordasco Ian Cordasco 3 | Ian Stapleton Cordasco Ian Cordasco 4 | Ian Stapleton Cordasco Ian Cordasco 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.5.0 4 | hooks: 5 | - id: check-yaml 6 | - id: debug-statements 7 | - id: end-of-file-fixer 8 | - id: trailing-whitespace 9 | exclude: ^tests/fixtures/ 10 | - repo: https://github.com/asottile/setup-cfg-fmt 11 | rev: v2.5.0 12 | hooks: 13 | - id: setup-cfg-fmt 14 | - repo: https://github.com/asottile/reorder-python-imports 15 | rev: v3.14.0 16 | hooks: 17 | - id: reorder-python-imports 18 | args: [ 19 | --application-directories, '.:src', 20 | --py39-plus, 21 | --add-import, 'from __future__ import annotations', 22 | ] 23 | - repo: https://github.com/asottile/pyupgrade 24 | rev: v3.19.1 25 | hooks: 26 | - id: pyupgrade 27 | args: [--py39-plus] 28 | - repo: https://github.com/psf/black 29 | rev: 23.12.1 30 | hooks: 31 | - id: black 32 | args: [--line-length=79] 33 | - repo: https://github.com/PyCQA/flake8 34 | rev: 7.0.0 35 | hooks: 36 | - id: flake8 37 | - repo: https://github.com/pre-commit/mirrors-mypy 38 | rev: v1.15.0 39 | hooks: 40 | - id: mypy 41 | exclude: ^(docs/|example-plugin/) 42 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: flake8 2 | name: flake8 3 | description: '`flake8` is a command-line utility for enforcing style consistency across Python projects.' 4 | entry: flake8 5 | language: python 6 | types: [python] 7 | require_serial: true 8 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | python: 8 | install: 9 | - path: . 10 | - requirements: docs/source/requirements.txt 11 | sphinx: 12 | configuration: docs/source/conf.py 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Please refer to `Contributing to Flake8 2 | `_ 3 | on our website. 4 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | Project created by Tarek Ziadé. 2 | 3 | Contributors (by order of appearance) : 4 | 5 | - Tamás Gulácsi 6 | - Nicolas Dumazet 7 | - Stefan Scherfke 8 | - Chris Adams 9 | - Ben Bass 10 | - Ask Solem 11 | - Steven Kryskalla 12 | - Gustavo Picon 13 | - Jannis Leidel 14 | - Miki Tebeka 15 | - David Cramer 16 | - Peter Teichman 17 | - Ian Cordasco 18 | - Oleg Broytman 19 | - Marc Labbé 20 | - Bruno Miguel Custódio 21 | - Florent Xicluna 22 | - Austin Morton 23 | - Michael McNeil Forbes 24 | - Christian Long 25 | - Tyrel Souza 26 | - Corey Farwell 27 | - Michael Penkov 28 | - Anthony Sottile 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | == Flake8 License (MIT) == 2 | 3 | Copyright (C) 2011-2013 Tarek Ziade 4 | Copyright (C) 2012-2016 Ian Cordasco 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://github.com/PyCQA/flake8/workflows/main/badge.svg 2 | :target: https://github.com/PyCQA/flake8/actions?query=workflow%3Amain 3 | :alt: build status 4 | 5 | .. image:: https://results.pre-commit.ci/badge/github/PyCQA/flake8/main.svg 6 | :target: https://results.pre-commit.ci/latest/github/PyCQA/flake8/main 7 | :alt: pre-commit.ci status 8 | 9 | .. image:: https://img.shields.io/discord/825463413634891776.svg 10 | :target: https://discord.gg/qYxpadCgkx 11 | :alt: Discord 12 | 13 | ======== 14 | Flake8 15 | ======== 16 | 17 | Flake8 is a wrapper around these tools: 18 | 19 | - PyFlakes 20 | - pycodestyle 21 | - Ned Batchelder's McCabe script 22 | 23 | Flake8 runs all the tools by launching the single ``flake8`` command. 24 | It displays the warnings in a per-file, merged output. 25 | 26 | It also adds a few features: 27 | 28 | - files that contain this line are skipped:: 29 | 30 | # flake8: noqa 31 | 32 | - lines that contain a ``# noqa`` comment at the end will not issue warnings. 33 | - you can ignore specific errors on a line with ``# noqa: ``, e.g., 34 | ``# noqa: E234``. Multiple codes can be given, separated by comma. The ``noqa`` token is case insensitive, the colon before the list of codes is required otherwise the part after ``noqa`` is ignored 35 | - Git and Mercurial hooks 36 | - extendable through ``flake8.extension`` and ``flake8.formatting`` entry 37 | points 38 | 39 | 40 | Quickstart 41 | ========== 42 | 43 | See our `quickstart documentation 44 | `_ for how to install 45 | and get started with Flake8. 46 | 47 | 48 | Frequently Asked Questions 49 | ========================== 50 | 51 | Flake8 maintains an `FAQ `_ in its 52 | documentation. 53 | 54 | 55 | Questions or Feedback 56 | ===================== 57 | 58 | If you have questions you'd like to ask the developers, or feedback you'd like 59 | to provide, feel free to use the mailing list: code-quality@python.org 60 | 61 | We would love to hear from you. Additionally, if you have a feature you'd like 62 | to suggest, the mailing list would be the best place for it. 63 | 64 | 65 | Links 66 | ===== 67 | 68 | * `Flake8 Documentation `_ 69 | 70 | * `GitHub Project `_ 71 | 72 | * `All (Open and Closed) Issues 73 | `_ 74 | 75 | * `Code-Quality Archives 76 | `_ 77 | 78 | * `Code of Conduct 79 | `_ 80 | 81 | * `Getting Started Contributing 82 | `_ 83 | 84 | 85 | Maintenance 86 | =========== 87 | 88 | Flake8 was created by Tarek Ziadé and is currently maintained by `anthony sottile 89 | `_ and `Ian Cordasco 90 | `_ 91 | -------------------------------------------------------------------------------- /bin/gen-pycodestyle-plugin: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from __future__ import annotations 3 | 4 | import inspect 5 | import os.path 6 | from collections.abc import Generator 7 | from typing import Any 8 | from typing import Callable 9 | from typing import NamedTuple 10 | 11 | import pycodestyle 12 | 13 | 14 | def _too_long(s: str) -> str: 15 | if len(s) >= 80: 16 | return f"{s} # noqa: E501" 17 | else: 18 | return s 19 | 20 | 21 | class Call(NamedTuple): 22 | name: str 23 | is_generator: bool 24 | params: tuple[str, ...] 25 | 26 | def to_src(self) -> str: 27 | params_s = ", ".join(self.params) 28 | if self.is_generator: 29 | return _too_long(f" yield from _{self.name}({params_s})") 30 | else: 31 | lines = ( 32 | _too_long(f" ret = _{self.name}({params_s})"), 33 | " if ret is not None:", 34 | " yield ret", 35 | ) 36 | return "\n".join(lines) 37 | 38 | @classmethod 39 | def from_func(cls, func: Callable[..., Any]) -> Call: 40 | spec = inspect.getfullargspec(func) 41 | params = tuple(spec.args) 42 | return cls(func.__name__, inspect.isgeneratorfunction(func), params) 43 | 44 | 45 | def lines() -> Generator[str]: 46 | logical = [] 47 | physical = [] 48 | 49 | logical = [ 50 | Call.from_func(check) for check in pycodestyle._checks["logical_line"] 51 | ] 52 | physical = [ 53 | Call.from_func(check) for check in pycodestyle._checks["physical_line"] 54 | ] 55 | assert not pycodestyle._checks["tree"] 56 | 57 | yield f'"""Generated using ./bin/{os.path.basename(__file__)}."""' 58 | yield "# fmt: off" 59 | yield "from __future__ import annotations" 60 | yield "" 61 | yield "from collections.abc import Generator" 62 | yield "from typing import Any" 63 | yield "" 64 | imports = sorted(call.name for call in logical + physical) 65 | for name in imports: 66 | yield _too_long(f"from pycodestyle import {name} as _{name}") 67 | yield "" 68 | yield "" 69 | 70 | yield "def pycodestyle_logical(" 71 | logical_params = {param for call in logical for param in call.params} 72 | for param in sorted(logical_params): 73 | yield f" {param}: Any," 74 | yield ") -> Generator[tuple[int, str]]:" 75 | yield ' """Run pycodestyle logical checks."""' 76 | for call in sorted(logical): 77 | yield call.to_src() 78 | yield "" 79 | yield "" 80 | 81 | yield "def pycodestyle_physical(" 82 | physical_params = {param for call in physical for param in call.params} 83 | for param in sorted(physical_params): 84 | yield f" {param}: Any," 85 | yield ") -> Generator[tuple[int, str]]:" 86 | yield ' """Run pycodestyle physical checks."""' 87 | for call in sorted(physical): 88 | yield call.to_src() 89 | 90 | 91 | def main() -> int: 92 | for line in lines(): 93 | print(line) 94 | return 0 95 | 96 | 97 | if __name__ == "__main__": 98 | raise SystemExit(main()) 99 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | tox 2 | -------------------------------------------------------------------------------- /docs/build/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/docs/build/.keep -------------------------------------------------------------------------------- /docs/source/faq.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Frequently Asked Questions 3 | ============================ 4 | 5 | When is Flake8 released? 6 | ======================== 7 | 8 | |Flake8| is released *as necessary*. Sometimes there are specific goals and 9 | drives to get to a release. Usually, we release as users report and fix 10 | bugs. 11 | 12 | 13 | How can I help Flake8 release faster? 14 | ===================================== 15 | 16 | Look at the next milestone. If there's work you can help us complete, that 17 | will help us get to the next milestone. If there's a show-stopping bug that 18 | needs to be released, let us know but please be kind. |Flake8| is developed 19 | and released entirely on volunteer time. 20 | 21 | 22 | What is the next version of Flake8? 23 | =================================== 24 | 25 | In general we try to use milestones to indicate this. If the last release 26 | on PyPI is 3.1.5 and you see a milestone for 3.2.0 in GitHub, there's a 27 | good chance that 3.2.0 is the next release. 28 | 29 | 30 | Why does Flake8 use ranges for its dependencies? 31 | ================================================ 32 | 33 | |Flake8| uses ranges for mccabe, pyflakes, and pycodestyle because each of 34 | those projects tend to add *new* checks in minor releases. It has been an 35 | implicit design goal of |Flake8|'s to make the list of error codes stable in 36 | its own minor releases. That way if you install something from the 2.5 37 | series today, you will not find new checks in the same series in a month 38 | from now when you install it again. 39 | 40 | |Flake8|'s dependencies tend to avoid new checks in patch versions which is 41 | why |Flake8| expresses its dependencies roughly as:: 42 | 43 | pycodestyle >= 2.0.0, < 2.1.0 44 | pyflakes >= 0.8.0, != 1.2.0, != 1.2.1, != 1.2.2, < 1.3.0 45 | mccabe >= 0.5.0, < 0.6.0 46 | 47 | This allows those projects to release patch versions that fix bugs and for 48 | |Flake8| users to consume those fixes. 49 | 50 | 51 | Should I file an issue when a new version of a dependency is available? 52 | ======================================================================= 53 | 54 | **No.** The current Flake8 core team (of one person) is also 55 | a core developer of pycodestyle, pyflakes, and mccabe. They are aware of 56 | these releases. 57 | -------------------------------------------------------------------------------- /docs/source/glossary.rst: -------------------------------------------------------------------------------- 1 | .. _glossary: 2 | 3 | ================================================ 4 | Glossary of Terms Used in Flake8 Documentation 5 | ================================================ 6 | 7 | .. glossary:: 8 | :sorted: 9 | 10 | formatter 11 | A :term:`plugin` that augments the output of |Flake8| when passed 12 | to :option:`flake8 --format`. 13 | 14 | plugin 15 | A package that is typically installed from PyPI to augment the 16 | behaviour of |Flake8| either through adding one or more additional 17 | :term:`check`\ s or providing additional :term:`formatter`\ s. 18 | 19 | check 20 | A piece of logic that corresponds to an error code. A check may 21 | be a style check (e.g., check the length of a given line against 22 | the user configured maximum) or a lint check (e.g., checking for 23 | unused imports) or some other check as defined by a plugin. 24 | 25 | error 26 | error code 27 | violation 28 | The symbol associated with a specific :term:`check`. For example, 29 | pycodestyle implements :term:`check`\ s that look for whitespace 30 | around binary operators and will either return an error code of 31 | ``W503`` or ``W504``. 32 | 33 | warning 34 | Typically the ``W`` class of :term:`error code`\ s from pycodestyle. 35 | 36 | class 37 | error class 38 | A larger grouping of related :term:`error code`\ s. For example, 39 | ``W503`` and ``W504`` are two codes related to whitespace. ``W50`` 40 | would be the most specific class of codes relating to whitespace. 41 | ``W`` would be the warning class that subsumes all whitespace 42 | errors. 43 | 44 | pyflakes 45 | The project |Flake8| depends on to lint files (check for unused 46 | imports, variables, etc.). This uses the ``F`` :term:`class` of 47 | :term:`error code`\ s reported by |Flake8|. 48 | 49 | pycodestyle 50 | The project |Flake8| depends on to provide style enforcement. 51 | pycodestyle implements :term:`check`\ s for :pep:`8`. This uses the 52 | ``E`` and ``W`` :term:`class`\ es of :term:`error code`\ s. 53 | 54 | mccabe 55 | The project |Flake8| depends on to calculate the McCabe complexity 56 | of a unit of code (e.g., a function). This uses the ``C`` 57 | :term:`class` of :term:`error code`\ s. 58 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. flake8 documentation master file, created by 2 | sphinx-quickstart on Tue Jan 19 07:14:10 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | =============================================== 7 | Flake8: Your Tool For Style Guide Enforcement 8 | =============================================== 9 | 10 | Quickstart 11 | ========== 12 | 13 | .. _installation-guide: 14 | 15 | Installation 16 | ------------ 17 | 18 | To install |Flake8|, open an interactive shell and run: 19 | 20 | .. code:: 21 | 22 | python -m pip install flake8 23 | 24 | If you want |Flake8| to be installed for your default Python installation, you 25 | can instead use: 26 | 27 | .. code:: 28 | 29 | python -m pip install flake8 30 | 31 | .. note:: 32 | 33 | It is **very** important to install |Flake8| on the *correct* version of 34 | Python for your needs. If you want |Flake8| to properly parse new language 35 | features in Python 3.5 (for example), you need it to be installed on 3.5 36 | for |Flake8| to understand those features. In many ways, Flake8 is tied to 37 | the version of Python on which it runs. 38 | 39 | Using Flake8 40 | ------------ 41 | 42 | To start using |Flake8|, open an interactive shell and run: 43 | 44 | .. code:: 45 | 46 | flake8 path/to/code/to/check.py 47 | # or 48 | flake8 path/to/code/ 49 | 50 | .. note:: 51 | 52 | If you have installed |Flake8| on a particular version of Python (or on 53 | several versions), it may be best to instead run ``python -m 54 | flake8``. 55 | 56 | If you only want to see the instances of a specific warning or error, you can 57 | *select* that error like so: 58 | 59 | .. code:: 60 | 61 | flake8 --select E123,W503 path/to/code/ 62 | 63 | Alternatively, if you want to add a specific warning or error to *ignore*: 64 | 65 | .. code:: 66 | 67 | flake8 --extend-ignore E203,W234 path/to/code/ 68 | 69 | Please read our user guide for more information about how to use and configure 70 | |Flake8|. 71 | 72 | FAQ and Glossary 73 | ================ 74 | 75 | .. toctree:: 76 | :maxdepth: 2 77 | 78 | faq 79 | glossary 80 | 81 | User Guide 82 | ========== 83 | 84 | All users of |Flake8| should read this portion of the documentation. This 85 | provides examples and documentation around |Flake8|'s assortment of options 86 | and how to specify them on the command-line or in configuration files. 87 | 88 | .. toctree:: 89 | :maxdepth: 2 90 | 91 | user/index 92 | 93 | Plugin Developer Guide 94 | ====================== 95 | 96 | If you're maintaining a plugin for |Flake8| or creating a new one, you should 97 | read this section of the documentation. It explains how you can write your 98 | plugins and distribute them to others. 99 | 100 | .. toctree:: 101 | :maxdepth: 2 102 | 103 | plugin-development/index 104 | 105 | Contributor Guide 106 | ================= 107 | 108 | If you are reading |Flake8|'s source code for fun or looking to contribute, 109 | you should read this portion of the documentation. This is a mix of documenting 110 | the internal-only interfaces |Flake8| and documenting reasoning for Flake8's 111 | design. 112 | 113 | .. toctree:: 114 | :maxdepth: 2 115 | 116 | internal/index 117 | 118 | Release Notes and History 119 | ========================= 120 | 121 | .. toctree:: 122 | :maxdepth: 2 123 | 124 | release-notes/index 125 | 126 | General Indices 127 | =============== 128 | 129 | * :ref:`genindex` 130 | * :ref:`Index of Documented Public Modules ` 131 | * :ref:`Glossary of terms ` 132 | -------------------------------------------------------------------------------- /docs/source/internal/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/docs/source/internal/.keep -------------------------------------------------------------------------------- /docs/source/internal/checker.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | How Checks are Run 3 | ==================== 4 | 5 | In |Flake8| 2.x, |Flake8| delegated check running to pep8. In 3.0 |Flake8| 6 | takes on that responsibility. This has allowed for simpler 7 | handling of the ``--jobs`` parameter (using :mod:`multiprocessing`) and 8 | simplified our fallback if something goes awry with concurrency. 9 | At the lowest level we have a |FileChecker|. Instances of |FileChecker| are 10 | created for *each* file to be analyzed by |Flake8|. Each instance, has a copy 11 | of all of the plugins registered with setuptools in the ``flake8.extension`` 12 | entry-point group. 13 | 14 | The |FileChecker| instances are managed by an instance of |Manager|. The 15 | |Manager| instance handles creating sub-processes with 16 | :mod:`multiprocessing` module and falling back to running checks in serial if 17 | an operating system level error arises. When creating |FileChecker| instances, 18 | the |Manager| is responsible for determining if a particular file has been 19 | excluded. 20 | 21 | 22 | Processing Files 23 | ---------------- 24 | 25 | Unfortunately, since |Flake8| took over check running from pep8/pycodestyle, 26 | it also had to take over parsing and processing files for the checkers 27 | to use. Since it couldn't reuse pycodestyle's functionality (since it did not 28 | separate cleanly the processing from check running) that function was isolated 29 | into the :class:`~flake8.processor.FileProcessor` class. We moved 30 | several helper functions into the :mod:`flake8.processor` module (see also 31 | :ref:`Processor Utility Functions `). 32 | 33 | 34 | API Reference 35 | ------------- 36 | 37 | .. autoclass:: flake8.checker.FileChecker 38 | :members: 39 | 40 | .. autoclass:: flake8.checker.Manager 41 | :members: 42 | 43 | .. autoclass:: flake8.processor.FileProcessor 44 | :members: 45 | 46 | 47 | .. _processor_utility_functions: 48 | 49 | Utility Functions 50 | ````````````````` 51 | 52 | .. autofunction:: flake8.processor.count_parentheses 53 | 54 | .. autofunction:: flake8.processor.expand_indent 55 | 56 | .. autofunction:: flake8.processor.is_eol_token 57 | 58 | .. autofunction:: flake8.processor.is_multiline_string 59 | 60 | .. autofunction:: flake8.processor.mutate_string 61 | 62 | .. autofunction:: flake8.processor.token_is_newline 63 | 64 | .. Substitutions 65 | .. |FileChecker| replace:: :class:`~flake8.checker.FileChecker` 66 | .. |Manager| replace:: :class:`~flake8.checker.Manager` 67 | -------------------------------------------------------------------------------- /docs/source/internal/cli.rst: -------------------------------------------------------------------------------- 1 | Command Line Interface 2 | ====================== 3 | 4 | The command line interface of |Flake8| is modeled as an application via 5 | :class:`~flake8.main.cli.Application`. When a user runs ``flake8`` at their 6 | command line, :func:`~flake8.main.cli.main` is run which handles 7 | management of the application. 8 | 9 | User input is parsed *twice* to accommodate logging and verbosity options 10 | passed by the user as early as possible. 11 | This is so as much logging can be produced as possible. 12 | 13 | The default |Flake8| options are registered by 14 | :func:`~flake8.main.options.register_default_options`. Trying to register 15 | these options in plugins will result in errors. 16 | 17 | 18 | API Documentation 19 | ----------------- 20 | 21 | .. autofunction:: flake8.main.cli.main 22 | 23 | .. autoclass:: flake8.main.application.Application 24 | :members: 25 | 26 | .. autofunction:: flake8.main.options.register_default_options 27 | -------------------------------------------------------------------------------- /docs/source/internal/formatters.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | Built-in Formatters 3 | ===================== 4 | 5 | By default |Flake8| has two formatters built-in, ``default`` and ``pylint``. 6 | These correspond to two classes |DefaultFormatter| and |PylintFormatter|. 7 | 8 | In |Flake8| 2.0, pep8 handled formatting of errors and also allowed users to 9 | specify an arbitrary format string as a parameter to ``--format``. In order 10 | to allow for this backwards compatibility, |Flake8| 3.0 made two choices: 11 | 12 | #. To not limit a user's choices for ``--format`` to the format class names 13 | 14 | #. To make the default formatter attempt to use the string provided by the 15 | user if it cannot find a formatter with that name. 16 | 17 | Default Formatter 18 | ================= 19 | 20 | The |DefaultFormatter| continues to use the same default format string as 21 | pep8: ``'%(path)s:%(row)d:%(col)d: %(code)s %(text)s'``. 22 | 23 | To provide the default functionality it overrides two methods: 24 | 25 | #. ``after_init`` 26 | 27 | #. ``format`` 28 | 29 | The former allows us to inspect the value provided to ``--format`` by the 30 | user and alter our own format based on that value. The second simply uses 31 | that format string to format the error. 32 | 33 | .. autoclass:: flake8.formatting.default.Default 34 | :members: 35 | 36 | Pylint Formatter 37 | ================ 38 | 39 | The |PylintFormatter| simply defines the default Pylint format string from 40 | pep8: ``'%(path)s:%(row)d: [%(code)s] %(text)s'``. 41 | 42 | .. autoclass:: flake8.formatting.default.Pylint 43 | :members: 44 | 45 | 46 | .. |DefaultFormatter| replace:: :class:`~flake8.formatting.default.Default` 47 | .. |PylintFormatter| replace:: :class:`~flake8.formatting.default.Pylint` 48 | -------------------------------------------------------------------------------- /docs/source/internal/index.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Exploring Flake8's Internals 3 | ============================== 4 | 5 | While writing |Flake8| 3.0, the developers attempted to capture some reasoning 6 | and decision information in internal documentation meant for future developers 7 | and maintainers. Most of this information is unnecessary for users and plugin 8 | developers. Some of it, however, is linked to from the plugin development 9 | documentation. 10 | 11 | Keep in mind that not everything will be here and you may need to help pull 12 | information out of the developers' heads and into these documents. Please 13 | pull gently. 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | 18 | contributing 19 | writing-documentation 20 | writing-code 21 | releases 22 | start-to-finish 23 | checker 24 | cli 25 | formatters 26 | option_handling 27 | plugin_handling 28 | utils 29 | -------------------------------------------------------------------------------- /docs/source/internal/plugin_handling.rst: -------------------------------------------------------------------------------- 1 | Plugin Handling 2 | =============== 3 | 4 | Plugin Management 5 | ----------------- 6 | 7 | |Flake8| 3.0 added support for other plugins besides those which define 8 | new checks. It now supports: 9 | 10 | - extra checks 11 | 12 | - alternative report formatters 13 | 14 | Default Plugins 15 | --------------- 16 | 17 | Finally, |Flake8| has always provided its own plugin shim for Pyflakes. As 18 | part of that we carry our own shim in-tree and now store that in 19 | :mod:`flake8.plugins.pyflakes`. 20 | 21 | |Flake8| also registers plugins for pycodestyle. Each check in pycodestyle 22 | requires different parameters and it cannot easily be shimmed together like 23 | Pyflakes was. As such, plugins have a concept of a "group". If you look at our 24 | :file:`setup.py` you will see that we register pycodestyle checks roughly like 25 | so: 26 | 27 | .. code:: 28 | 29 | pycodestyle. = pycodestyle: 30 | 31 | We do this to identify that ``>`` is part of a group. This also 32 | enables us to special-case how we handle reporting those checks. Instead of 33 | reporting each check in the ``--version`` output, we only report 34 | ``pycodestyle`` once. 35 | 36 | API Documentation 37 | ----------------- 38 | 39 | .. autofunction:: flake8.plugins.finder.parse_plugin_options 40 | 41 | .. autofunction:: flake8.plugins.finder.find_plugins 42 | 43 | .. autofunction:: flake8.plugins.finder.load_plugins 44 | -------------------------------------------------------------------------------- /docs/source/internal/releases.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Releasing Flake8 3 | ================== 4 | 5 | There is not much that is hard to find about how |Flake8| is released. 6 | 7 | - We use **major** releases (e.g., 2.0.0, 3.0.0, etc.) for big, potentially 8 | backwards incompatible, releases. 9 | 10 | - We use **minor** releases (e.g., 2.1.0, 2.2.0, 3.1.0, 3.2.0, etc.) for 11 | releases that contain features and dependency version changes. 12 | 13 | - We use **patch** releases (e.g., 2.1.1, 2.1.2, 3.0.1, 3.0.10, etc.) for 14 | releases that contain *only* bug fixes. 15 | 16 | In this sense we follow semantic versioning. But we follow it as more of a set 17 | of guidelines. We're also not perfect, so we may make mistakes, and that's 18 | fine. 19 | 20 | 21 | Major Releases 22 | ============== 23 | 24 | Major releases are often associated with backwards incompatibility. |Flake8| 25 | hopes to avoid those, but will occasionally need them. 26 | 27 | Historically, |Flake8| has generated major releases for: 28 | 29 | - Unvendoring dependencies (2.0) 30 | 31 | - Large scale refactoring (2.0, 3.0, 5.0, 6.0) 32 | 33 | - Subtly breaking CLI changes (3.0, 4.0, 5.0, 6.0, 7.0) 34 | 35 | - Breaking changes to its plugin interface (3.0) 36 | 37 | Major releases can also contain: 38 | 39 | - Bug fixes (which may have backwards incompatible solutions) 40 | 41 | - New features 42 | 43 | - Dependency changes 44 | 45 | 46 | Minor Releases 47 | ============== 48 | 49 | Minor releases often have new features in them, which we define roughly as: 50 | 51 | - New command-line flags 52 | 53 | - New behaviour that does not break backwards compatibility 54 | 55 | - New errors detected by dependencies, e.g., by raising the upper limit on 56 | PyFlakes we introduce F405 57 | 58 | - Bug fixes 59 | 60 | 61 | Patch Releases 62 | ============== 63 | 64 | Patch releases should only ever have bug fixes in them. 65 | 66 | We do not update dependency constraints in patch releases. If you do not 67 | install |Flake8| from PyPI, there is a chance that your packager is using 68 | different requirements. Some downstream redistributors have been known to 69 | force a new version of PyFlakes, pep8/PyCodestyle, or McCabe into place. 70 | Occasionally this will cause breakage when using |Flake8|. There is little 71 | we can do to help you in those cases. 72 | 73 | 74 | Process 75 | ======= 76 | 77 | To prepare a release, we create a file in :file:`docs/source/release-notes/` 78 | named: ``{{ release_number }}.rst`` (e.g., ``3.0.0.rst``). We note bug fixes, 79 | improvements, and dependency version changes as well as other items of note 80 | for users. 81 | 82 | Before releasing, the following tox test environments must pass: 83 | 84 | - Python 3.9 (a.k.a., ``tox -e py39``) 85 | 86 | - Python 3.13 (a.k.a., ``tox -e py313``) 87 | 88 | - PyPy 3 (a.k.a., ``tox -e pypy3``) 89 | 90 | - Linters (a.k.a., ``tox -e linters``) 91 | 92 | We tag the most recent commit that passes those items and contains our release 93 | notes. 94 | 95 | Finally, we run ``tox -e release`` to build source distributions (e.g., 96 | ``flake8-3.0.0.tar.gz``), universal wheels, and upload them to PyPI with 97 | Twine. 98 | -------------------------------------------------------------------------------- /docs/source/internal/start-to-finish.rst: -------------------------------------------------------------------------------- 1 | ================================== 2 | What Happens When You Run Flake8 3 | ================================== 4 | 5 | Given |Flake8| 3.0's new organization and structure, it might be a bit much 6 | for some people to understand what happens from when you call ``flake8`` on the 7 | command-line to when it completes. This section aims to give you something of 8 | a technical overview of what exactly happens. 9 | 10 | 11 | Invocation 12 | ========== 13 | 14 | The exact way that we end up in our ``main`` function for Flake8 depends on 15 | how you invoke it. If you do something like: 16 | 17 | .. prompt:: bash 18 | 19 | flake8 20 | 21 | Then your shell looks up where ``flake8`` the executable lives and executes 22 | it. In almost every case, this is a tiny python script generated by 23 | ``setuptools`` using the console script entry points that |Flake8| declares 24 | in its :file:`setup.py`. This might look something like: 25 | 26 | .. code-block:: python 27 | 28 | #!/path/to/python 29 | # EASY-INSTALL-ENTRY-SCRIPT: 'flake8==3.0.0','console_scripts','flake8' 30 | __requires__ = 'flake8==3.0.0' 31 | import sys 32 | from pkg_resources import load_entry_point 33 | 34 | if __name__ == '__main__': 35 | sys.exit( 36 | load_entry_point('flake8==3.0.0', 'console_scripts', 'flake8')() 37 | ) 38 | 39 | If instead you invoke it like: 40 | 41 | .. prompt:: bash 42 | 43 | python -m flake8 44 | 45 | Then you're relying on Python to find :mod:`flake8.__main__` and run that. In 46 | both cases, however, you end up in :func:`flake8.main.cli.main`. This is the 47 | primary way that users will end up starting Flake8. This function creates an 48 | instance of |Application|. 49 | 50 | Application Logic 51 | ================= 52 | 53 | When we create our |Application| instance, we record the start time and parse 54 | our command-line arguments so we can configure the verbosity of |Flake8|'s 55 | logging. For the most part, every path then calls 56 | :meth:`~flake8.main.application.Application.run` which in turn calls: 57 | 58 | - :meth:`~flake8.main.application.Application.initialize` 59 | - :meth:`~flake8.main.application.Application.run_checks` 60 | - :meth:`~flake8.main.application.Application.report_errors` 61 | - :meth:`~flake8.main.application.Application.report_benchmarks` 62 | 63 | Our Git hook, however, runs these individually. 64 | 65 | Application Initialization 66 | -------------------------- 67 | 68 | :meth:`~flake8.main.application.Application.initialize` loads all of our 69 | :term:`plugin`\ s, registers the options for those plugins, parses the 70 | command-line arguments, makes our formatter (as selected by the user), makes 71 | our :class:`~flake8.style_guide.StyleGuide` and finally makes our 72 | :class:`file checker manager `. 73 | 74 | Running Our Checks 75 | ------------------ 76 | 77 | :meth:`~flake8.main.application.Application.run_checks` then creates an 78 | instance of :class:`flake8.checker.FileChecker` for each file to be checked 79 | after aggregating all of the files that are not excluded and match the 80 | provided file-patterns. Then, if we're on a system that supports 81 | :mod:`multiprocessing` **and** :option:`flake8 --jobs` is either ``auto`` or 82 | a number greater than 1, we will begin processing the files in subprocesses. 83 | Otherwise, we'll run the checks in parallel. 84 | 85 | After we start running the checks, we start aggregating the reported 86 | :term:`violation`\ s in the main process. After the checks are done running, 87 | we record the end time. 88 | 89 | Reporting Violations 90 | -------------------- 91 | 92 | Next, the application takes the violations from the file checker manager, and 93 | feeds them through the :class:`~flake8.style_guide.StyleGuide`. This 94 | relies on a :class:`~flake8.style_guide.DecisionEngine` instance to determine 95 | whether the particular :term:`error code` is selected or ignored and then 96 | appropriately sends it to the formatter (or not). 97 | 98 | Reporting Benchmarks 99 | -------------------- 100 | 101 | Finally, if the user has asked to see benchmarks (i.e., :option:`flake8 102 | --benchmark`) then we print the benchmarks. 103 | 104 | 105 | Exiting 106 | ======= 107 | 108 | Once :meth:`~flake8.main.application.Application.run` has finished, we then 109 | call :meth:`~flake8.main.application.Application.exit` which looks at how 110 | many errors were reported and whether the user specified :option:`flake8 111 | --exit-zero` and exits with the appropriate exit code. 112 | 113 | 114 | .. Replacements 115 | .. |Application| replace:: :class:`~flake8.main.application.Application` 116 | -------------------------------------------------------------------------------- /docs/source/internal/utils.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | Utility Functions 3 | =================== 4 | 5 | |Flake8| has a few utility functions that it uses internally. 6 | 7 | .. warning:: 8 | 9 | As should be implied by where these are documented, these are all 10 | **internal** utility functions. Their signatures and return types 11 | may change between releases without notice. 12 | 13 | Bugs reported about these **internal** functions will be closed 14 | immediately. 15 | 16 | If functions are needed by plugin developers, they may be requested 17 | in the bug tracker and after careful consideration they *may* be added 18 | to the *documented* stable API. 19 | 20 | .. autofunction:: flake8.utils.parse_comma_separated_list 21 | 22 | :func:`~flake8.utils.parse_comma_separated_list` takes either a string like 23 | 24 | .. code-block:: python 25 | 26 | "E121,W123,F904" 27 | "E121,\nW123,\nF804" 28 | " E121,\n\tW123,\n\tF804 " 29 | " E121\n\tW123 \n\tF804" 30 | 31 | And converts it to a list that looks as follows 32 | 33 | .. code-block:: python 34 | 35 | ["E121", "W123", "F904"] 36 | 37 | This function helps normalize any kind of comma-separated input you or |Flake8| 38 | might receive. This is most helpful when taking advantage of |Flake8|'s 39 | additional parameters to :class:`~flake8.options.manager.Option`. 40 | 41 | .. autofunction:: flake8.utils.normalize_path 42 | 43 | This utility takes a string that represents a path and returns the absolute 44 | path if the string has a ``/`` in it. It also removes trailing ``/``\ s. 45 | 46 | .. autofunction:: flake8.utils.normalize_paths 47 | 48 | This function utilizes :func:`~flake8.utils.normalize_path` to normalize a 49 | sequence of paths. See :func:`~flake8.utils.normalize_path` for what defines a 50 | normalized path. 51 | 52 | .. autofunction:: flake8.utils.stdin_get_value 53 | 54 | This function retrieves and caches the value provided on ``sys.stdin``. This 55 | allows plugins to use this to retrieve ``stdin`` if necessary. 56 | 57 | .. autofunction:: flake8.utils.is_using_stdin 58 | 59 | Another helpful function that is named only to be explicit given it is a very 60 | trivial check, this checks if the user specified ``-`` in their arguments to 61 | |Flake8| to indicate we should read from stdin. 62 | 63 | .. autofunction:: flake8.utils.fnmatch 64 | 65 | The standard library's :func:`fnmatch.fnmatch` is excellent at deciding if a 66 | filename matches a single pattern. In our use case, however, we typically have 67 | a list of patterns and want to know if the filename matches any of them. This 68 | function abstracts that logic away with a little extra logic. 69 | -------------------------------------------------------------------------------- /docs/source/plugin-development/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/docs/source/plugin-development/.keep -------------------------------------------------------------------------------- /docs/source/plugin-development/formatters.rst: -------------------------------------------------------------------------------- 1 | .. _formatting-plugins: 2 | 3 | =========================================== 4 | Developing a Formatting Plugin for Flake8 5 | =========================================== 6 | 7 | |Flake8| allowed for custom formatting plugins in version 8 | 3.0.0. Let's write a plugin together: 9 | 10 | .. code-block:: python 11 | 12 | from flake8.formatting import base 13 | 14 | 15 | class Example(base.BaseFormatter): 16 | """Flake8's example formatter.""" 17 | 18 | pass 19 | 20 | We notice, as soon as we start, that we inherit from |Flake8|'s 21 | :class:`~flake8.formatting.base.BaseFormatter` class. If we follow the 22 | :ref:`instructions to register a plugin ` and try to use 23 | our example formatter, e.g., ``flake8 --format=example`` then 24 | |Flake8| will fail because we did not implement the ``format`` method. 25 | Let's do that next. 26 | 27 | .. code-block:: python 28 | 29 | class Example(base.BaseFormatter): 30 | """Flake8's example formatter.""" 31 | 32 | def format(self, error): 33 | return 'Example formatter: {0!r}'.format(error) 34 | 35 | With that we're done. Obviously this isn't a very useful formatter, but it 36 | should highlight the simplicity of creating a formatter with Flake8. If we 37 | wanted to instead create a formatter that aggregated the results and returned 38 | XML, JSON, or subunit we could also do that. |Flake8| interacts with the 39 | formatter in two ways: 40 | 41 | #. It creates the formatter and provides it the options parsed from the 42 | configuration files and command-line 43 | 44 | #. It uses the instance of the formatter and calls ``handle`` with the error. 45 | 46 | By default :meth:`flake8.formatting.base.BaseFormatter.handle` simply calls 47 | the ``format`` method and then ``write``. Any extra handling you wish to do 48 | for formatting purposes should override the ``handle`` method. 49 | 50 | API Documentation 51 | ================= 52 | 53 | .. autoclass:: flake8.formatting.base.BaseFormatter 54 | :members: 55 | -------------------------------------------------------------------------------- /docs/source/plugin-development/index.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Writing Plugins for Flake8 3 | ============================ 4 | 5 | Since |Flake8| 2.0, the |Flake8| tool has allowed for extensions and custom 6 | plugins. In |Flake8| 3.0, we're expanding that ability to customize and 7 | extend **and** we're attempting to thoroughly document it. Some of the 8 | documentation in this section may reference third-party documentation to 9 | reduce duplication and to point you, the developer, towards the authoritative 10 | documentation for those pieces. 11 | 12 | Getting Started 13 | =============== 14 | 15 | To get started writing a |Flake8| :term:`plugin` you first need: 16 | 17 | - An idea for a plugin 18 | 19 | - An available package name on PyPI 20 | 21 | - One or more versions of Python installed 22 | 23 | - A text editor or IDE of some kind 24 | 25 | - An idea of what *kind* of plugin you want to build: 26 | 27 | * Formatter 28 | 29 | * Check 30 | 31 | Once you've gathered these things, you can get started. 32 | 33 | All plugins for |Flake8| must be registered via 34 | :external+packaging:doc:`entry points`. In this 35 | section we cover: 36 | 37 | - How to register your plugin so |Flake8| can find it 38 | 39 | - How to make |Flake8| provide your check plugin with information (via 40 | command-line flags, function/class parameters, etc.) 41 | 42 | - How to make a formatter plugin 43 | 44 | - How to write your check plugin so that it works with |Flake8| 2.x and 3.x 45 | 46 | 47 | Video Tutorial 48 | ============== 49 | 50 | Here's a tutorial which goes over building an ast checking plugin from scratch: 51 | 52 | .. raw:: html 53 | 54 |
55 | 56 |
57 | 58 | Detailed Plugin Development Documentation 59 | ========================================= 60 | 61 | .. toctree:: 62 | :caption: Plugin Developer Documentation 63 | :maxdepth: 2 64 | 65 | registering-plugins 66 | plugin-parameters 67 | formatters 68 | -------------------------------------------------------------------------------- /docs/source/plugin-development/registering-plugins.rst: -------------------------------------------------------------------------------- 1 | .. _register-a-plugin: 2 | 3 | ================================== 4 | Registering a Plugin with Flake8 5 | ================================== 6 | 7 | To register any kind of plugin with |Flake8|, you need: 8 | 9 | #. A way to install the plugin (whether it is packaged on its own or 10 | as part of something else). In this section, we will use a ``setup.py`` 11 | written for an example plugin. 12 | 13 | #. A name for your plugin that will (ideally) be unique. 14 | 15 | |Flake8| relies on functionality provided by build tools called 16 | :external+packaging:doc:`entry points`. These 17 | allow any package to register a plugin with |Flake8| via that package's 18 | metadata. 19 | 20 | Let's presume that we already have our plugin written and it's in a module 21 | called ``flake8_example``. We will also assume ``setuptools`` is used as a 22 | :external+packaging:term:`Build Backend`, but be aware that most backends 23 | support entry points. 24 | 25 | We might have a ``setup.py`` that looks something like: 26 | 27 | .. code-block:: python 28 | 29 | import setuptools 30 | 31 | requires = [ 32 | "flake8 > 3.0.0", 33 | ] 34 | 35 | flake8_entry_point = # ... 36 | 37 | setuptools.setup( 38 | name="flake8_example", 39 | license="MIT", 40 | version="0.1.0", 41 | description="our extension to flake8", 42 | author="Me", 43 | author_email="example@example.com", 44 | url="https://github.com/me/flake8_example", 45 | packages=[ 46 | "flake8_example", 47 | ], 48 | install_requires=requires, 49 | entry_points={ 50 | flake8_entry_point: [ 51 | 'X = flake8_example:ExamplePlugin', 52 | ], 53 | }, 54 | classifiers=[ 55 | "Framework :: Flake8", 56 | "Environment :: Console", 57 | "Intended Audience :: Developers", 58 | "License :: OSI Approved :: MIT License", 59 | "Programming Language :: Python", 60 | "Programming Language :: Python :: 3", 61 | "Topic :: Software Development :: Libraries :: Python Modules", 62 | "Topic :: Software Development :: Quality Assurance", 63 | ], 64 | ) 65 | 66 | Note specifically these lines: 67 | 68 | .. code-block:: python 69 | 70 | flake8_entry_point = # ... 71 | 72 | setuptools.setup( 73 | # snip ... 74 | entry_points={ 75 | flake8_entry_point: [ 76 | 'X = flake8_example:ExamplePlugin', 77 | ], 78 | }, 79 | # snip ... 80 | ) 81 | 82 | We tell setuptools to register our entry point ``X`` inside the specific 83 | grouping of entry-points that flake8 should look in. 84 | 85 | |Flake8| presently looks at two groups: 86 | 87 | - ``flake8.extension`` 88 | 89 | - ``flake8.report`` 90 | 91 | If your plugin is one that adds checks to |Flake8|, you will use 92 | ``flake8.extension``. If your plugin performs extra report 93 | handling (formatting, filtering, etc.) it will use ``flake8.report``. 94 | 95 | If our ``ExamplePlugin`` is something that adds checks, our code would look 96 | like: 97 | 98 | .. code-block:: python 99 | 100 | setuptools.setup( 101 | # snip ... 102 | entry_points={ 103 | 'flake8.extension': [ 104 | 'X = flake8_example:ExamplePlugin', 105 | ], 106 | }, 107 | # snip ... 108 | ) 109 | 110 | The ``X`` in checking plugins define what error codes it is going to report. 111 | So if the plugin reports only the error code ``X101`` your entry-point would 112 | look like:: 113 | 114 | X101 = flake8_example:ExamplePlugin 115 | 116 | In the above case, the entry-point name and the error code produced by your 117 | plugin are the same. 118 | 119 | If your plugin reports several error codes that all start with ``X10``, then 120 | it would look like:: 121 | 122 | X10 = flake8_example:ExamplePlugin 123 | 124 | In this case as well as the following case, your entry-point name acts as 125 | a prefix to the error codes produced by your plugin. 126 | 127 | If all of your plugin's error codes start with ``X1`` then it would look 128 | like:: 129 | 130 | X1 = flake8_example:ExamplePlugin 131 | 132 | Finally, if all of your plugin's error codes start with just ``X`` then it 133 | would look like the original example. 134 | 135 | |Flake8| requires each entry point to be unique amongst all plugins installed 136 | in the users environment. Selecting an entry point that is already used can 137 | cause plugins to be deactivated without warning! 138 | 139 | **Please Note:** Your entry point does not need to be exactly 4 characters 140 | as of |Flake8| 3.0. Single letter entry point prefixes (such as the 141 | 'X' in the examples above) have caused issues in the past. As such, 142 | please consider using a 2 or 3 character entry point prefix, 143 | i.e., ``ABC`` is better than ``A`` but ``ABCD`` is invalid. 144 | *A 3 letters entry point prefix followed by 3 numbers (i.e.* ``ABC123`` *) 145 | is currently the longest allowed entry point name.* 146 | 147 | .. _off-by-default: 148 | 149 | If your plugin is intended to be opt-in, it can set the attribute 150 | ``off_by_default = True``. Users of your plugin will then need to utilize 151 | :ref:`enable-extensions` with your plugin's entry 152 | point. 153 | 154 | .. seealso:: 155 | 156 | The :external+setuptools:doc:`setuptools user guide ` 157 | about entry points. 158 | -------------------------------------------------------------------------------- /docs/source/release-notes/0.6.0.rst: -------------------------------------------------------------------------------- 1 | 0.6 - 2010-02-15 2 | ---------------- 3 | 4 | - Fix the McCabe metric on some loops 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/0.7.0.rst: -------------------------------------------------------------------------------- 1 | 0.7 - 2010-02-18 2 | ---------------- 3 | 4 | - Fix pep8 initialization when run through Hg 5 | - Make pep8 short options work when run through the command line 6 | - Skip duplicates when controlling files via Hg 7 | -------------------------------------------------------------------------------- /docs/source/release-notes/0.8.0.rst: -------------------------------------------------------------------------------- 1 | 0.8 - 2011-02-27 2 | ---------------- 3 | 4 | - fixed hg hook 5 | - discard unexisting files on hook check 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/0.9.0.rst: -------------------------------------------------------------------------------- 1 | 0.9 - 2011-11-09 2 | ---------------- 3 | 4 | - update pep8 version to 0.6.1 5 | - mccabe check: gracefully handle compile failure 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.0.0.rst: -------------------------------------------------------------------------------- 1 | 1.0 - 2011-11-29 2 | ---------------- 3 | 4 | - Deactivates by default the complexity checker 5 | - Introduces the complexity option in the HG hook and the command line. 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.1.0.rst: -------------------------------------------------------------------------------- 1 | 1.1 - 2012-02-14 2 | ---------------- 3 | 4 | - fixed the value returned by --version 5 | - allow the flake8: header to be more generic 6 | - fixed the "hg hook raises 'physical lines'" bug 7 | - allow three argument form of raise 8 | - now uses setuptools if available, for 'develop' command 9 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.2.0.rst: -------------------------------------------------------------------------------- 1 | 1.2 - 2012-02-12 2 | ---------------- 3 | 4 | - added a git hook 5 | - now Python 3 compatible 6 | - mccabe and pyflakes have warning codes like pep8 now 7 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.3.0.rst: -------------------------------------------------------------------------------- 1 | 1.3 - 2012-03-12 2 | ---------------- 3 | 4 | - fixed false W402 warning on exception blocks. 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.3.1.rst: -------------------------------------------------------------------------------- 1 | 1.3.1 - 2012-05-19 2 | ------------------ 3 | 4 | - fixed support for Python 2.5 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.4.0.rst: -------------------------------------------------------------------------------- 1 | 1.4 - 2012-07-12 2 | ---------------- 3 | 4 | - git_hook: Only check staged changes for compliance 5 | - use pep8 1.2 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.5.0.rst: -------------------------------------------------------------------------------- 1 | 1.5 - 2012-10-13 2 | ---------------- 3 | 4 | - fixed the stdin 5 | - make sure mccabe catches the syntax errors as warnings 6 | - pep8 upgrade 7 | - added max_line_length default value 8 | - added Flake8Command and entry points if setuptools is around 9 | - using the setuptools console wrapper when available 10 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.6.0.rst: -------------------------------------------------------------------------------- 1 | 1.6 - 2012-11-16 2 | ---------------- 3 | 4 | - changed the signatures of the ``check_file`` function in flake8/run.py, 5 | ``skip_warning`` in flake8/util.py and the ``check``, ``checkPath`` 6 | functions in flake8/pyflakes.py. 7 | - fix ``--exclude`` and ``--ignore`` command flags (#14, #19) 8 | - fix the git hook that wasn't catching files not already added to the index 9 | (#29) 10 | - pre-emptively includes the addition to pep8 to ignore certain lines. 11 | Add ``# nopep8`` to the end of a line to ignore it. (#37) 12 | - ``check_file`` can now be used without any special prior setup (#21) 13 | - unpacking exceptions will no longer cause an exception (#20) 14 | - fixed crash on non-existent file (#38) 15 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.6.1.rst: -------------------------------------------------------------------------------- 1 | 1.6.1 - 2012-11-24 2 | ------------------ 3 | 4 | - fixed the mercurial hook, a change from a previous patch was not properly 5 | applied 6 | - fixed an assumption about warnings/error messages that caused an exception 7 | to be thrown when McCabe is used 8 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.6.2.rst: -------------------------------------------------------------------------------- 1 | 1.6.2 - 2012-11-25 2 | ------------------ 3 | 4 | - fixed the NameError: global name 'message' is not defined (#46) 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/1.7.0.rst: -------------------------------------------------------------------------------- 1 | 1.7.0 - 2012-12-21 2 | ------------------ 3 | 4 | - Fixes part of #35: Exception for no WITHITEM being an attribute of Checker 5 | for Python 3.3 6 | - Support stdin 7 | - Incorporate @phd's builtins pull request 8 | - Fix the git hook 9 | - Update pep8.py to the latest version 10 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.0.0.rst: -------------------------------------------------------------------------------- 1 | 2.0.0 - 2013-02-23 2 | ------------------ 3 | 4 | - Pyflakes errors are prefixed by an ``F`` instead of an ``E`` 5 | - McCabe complexity warnings are prefixed by a ``C`` instead of a ``W`` 6 | - Flake8 supports extensions through entry points 7 | - Due to the above support, we **require** setuptools 8 | - We publish the `documentation `_ 9 | - Fixes #13: pep8, pyflakes and mccabe become external dependencies 10 | - Split run.py into main.py, engine.py and hooks.py for better logic 11 | - Expose our parser for our users 12 | - New feature: Install git and hg hooks automagically 13 | - By relying on pyflakes (0.6.1), we also fixed #45 and #35 14 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.1.0.rst: -------------------------------------------------------------------------------- 1 | 2.1.0 - 2013-10-26 2 | ------------------ 3 | 4 | - Add FLAKE8_LAZY and FLAKE8_IGNORE environment variable support to git and 5 | mercurial hooks 6 | - Force git and mercurial hooks to repsect configuration in setup.cfg 7 | - Only check staged files if that is specified 8 | - Fix hook file permissions 9 | - Fix the git hook on python 3 10 | - Ignore non-python files when running the git hook 11 | - Ignore .tox directories by default 12 | - Flake8 now reports the column number for PyFlakes messages 13 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.2.0.rst: -------------------------------------------------------------------------------- 1 | 2.2.0 - 2014-06-22 2 | ------------------ 3 | 4 | - New option ``doctests`` to run Pyflakes checks on doctests too 5 | - New option ``jobs`` to launch multiple jobs in parallel 6 | - Turn on using multiple jobs by default using the CPU count 7 | - Add support for ``python -m flake8`` on Python 2.7 and Python 3 8 | - Fix Git and Mercurial hooks: issues #88, #133, #148 and #149 9 | - Fix crashes with Python 3.4 by upgrading dependencies 10 | - Fix traceback when running tests with Python 2.6 11 | - Fix the setuptools command ``python setup.py flake8`` to read 12 | the project configuration 13 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.2.1.rst: -------------------------------------------------------------------------------- 1 | 2.2.1 - 2014-06-30 2 | ------------------ 3 | 4 | - Turn off multiple jobs by default. To enable automatic use of all CPUs, use 5 | ``--jobs=auto``. Fixes #155 and #154. 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.2.2.rst: -------------------------------------------------------------------------------- 1 | 2.2.2 - 2014-07-04 2 | ------------------ 3 | 4 | - Re-enable multiprocessing by default while fixing the issue Windows users 5 | were seeing. 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.2.3.rst: -------------------------------------------------------------------------------- 1 | 2.2.3 - 2014-08-25 2 | ------------------ 3 | 4 | - Actually turn multiprocessing on by default 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.2.4.rst: -------------------------------------------------------------------------------- 1 | 2.2.4 - 2014-10-09 2 | ------------------ 3 | 4 | - Fix bugs triggered by turning multiprocessing on by default (again) 5 | 6 | Multiprocessing is forcibly disabled in the following cases: 7 | 8 | - Passing something in via stdin 9 | 10 | - Analyzing a diff 11 | 12 | - Using windows 13 | 14 | - Fix --install-hook when there are no config files present for pep8 or 15 | flake8. 16 | 17 | - Fix how the setuptools command parses excludes in config files 18 | 19 | - Fix how the git hook determines which files to analyze (Thanks Chris 20 | Buccella!) 21 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.2.5.rst: -------------------------------------------------------------------------------- 1 | 2.2.5 - 2014-10-19 2 | ------------------ 3 | 4 | - Flush standard out when using multiprocessing 5 | 6 | - Make the check for "# flake8: noqa" more strict 7 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.3.0.rst: -------------------------------------------------------------------------------- 1 | 2.3.0 - 2015-01-04 2 | ------------------ 3 | 4 | - **Feature**: Add ``--output-file`` option to specify a file to write to 5 | instead of ``stdout``. 6 | 7 | - **Bug** Fix interleaving of output while using multiprocessing 8 | (:issue:`60`) 9 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.4.0.rst: -------------------------------------------------------------------------------- 1 | 2.4.0 - 2015-03-07 2 | ------------------ 3 | 4 | - **Bug** Print filenames when using multiprocessing and ``-q`` option. 5 | (:issue:`74`) 6 | 7 | - **Bug** Put upper cap on dependencies. The caps for 2.4.0 are: 8 | 9 | - ``pep8 < 1.6`` (Related to :issue:`78`) 10 | 11 | - ``mccabe < 0.4`` 12 | 13 | - ``pyflakes < 0.9`` 14 | 15 | See also :issue:`75` 16 | 17 | - **Bug** Files excluded in a config file were not being excluded when flake8 18 | was run from a git hook. (:issue:`2`) 19 | 20 | - **Improvement** Print warnings for users who are providing mutually 21 | exclusive options to flake8. (:issue:`51`, :issue:`386`) 22 | 23 | - **Feature** Allow git hook configuration to live in ``.git/config``. 24 | See the updated `VCS hooks docs`_ for more details. (:issue:`387`) 25 | 26 | .. _VCS hooks docs: https://flake8.readthedocs.io/en/latest/user/using-hooks.html 27 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.4.1.rst: -------------------------------------------------------------------------------- 1 | 2.4.1 - 2015-05-18 2 | ------------------ 3 | 4 | - **Bug** Do not raise a ``SystemError`` unless there were errors in the 5 | setuptools command. (:issue:`82`, :issue:`390`) 6 | 7 | - **Bug** Do not verify dependencies of extensions loaded via entry-points. 8 | 9 | - **Improvement** Blacklist versions of pep8 we know are broken 10 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.5.0.rst: -------------------------------------------------------------------------------- 1 | 2.5.0 - 2015-10-26 2 | ------------------ 3 | 4 | - **Improvement** Raise cap on PyFlakes for Python 3.5 support 5 | 6 | - **Improvement** Avoid deprecation warnings when loading extensions 7 | (:issue:`102`, :issue:`445`) 8 | 9 | - **Improvement** Separate logic to enable "off-by-default" extensions 10 | (:issue:`110`) 11 | 12 | - **Bug** Properly parse options to setuptools Flake8 command (:issue:`408`) 13 | 14 | - **Bug** Fix exceptions when output on stdout is truncated before Flake8 15 | finishes writing the output (:issue:`112`) 16 | 17 | - **Bug** Fix error on OS X where Flake8 can no longer acquire or create new 18 | semaphores (:issue:`117`) 19 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.5.1.rst: -------------------------------------------------------------------------------- 1 | 2.5.1 - 2015-12-08 2 | ------------------ 3 | 4 | - **Bug** Properly look for ``.flake8`` in current working directory 5 | (:issue:`458`) 6 | 7 | - **Bug** Monkey-patch ``pep8.stdin_get_value`` to cache the actual value in 8 | stdin. This helps plugins relying on the function when run with 9 | multiprocessing. (:issue:`460`, :issue:`462`) 10 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.5.2.rst: -------------------------------------------------------------------------------- 1 | 2.5.2 - 2016-01-30 2 | ------------------ 3 | 4 | - **Bug** Parse ``output_file`` and ``enable_extensions`` from config files 5 | 6 | - **Improvement** Raise upper bound on mccabe plugin to allow for version 7 | 0.4.0 8 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.5.3.rst: -------------------------------------------------------------------------------- 1 | 2.5.3 - 2016-02-11 2 | ------------------ 3 | 4 | - **Bug** Actually parse ``output_file`` and ``enable_extensions`` from config 5 | files 6 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.5.4.rst: -------------------------------------------------------------------------------- 1 | 2.5.4 - 2016-02-11 2 | ------------------ 3 | 4 | - **Bug** Missed an attribute rename during the v2.5.3 release. 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.5.5.rst: -------------------------------------------------------------------------------- 1 | 2.5.5 - 2016-06-14 2 | ------------------ 3 | 4 | - **Bug** Fix setuptools integration when parsing config files 5 | 6 | - **Bug** Don't pass the user's config path as the config_file when creating a 7 | StyleGuide 8 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.6.0.rst: -------------------------------------------------------------------------------- 1 | 2.6.0 - 2016-06-15 2 | ------------------ 3 | 4 | - **Requirements Change** Switch to pycodestyle as all future pep8 releases 5 | will use that package name 6 | 7 | - **Improvement** Allow for Windows users on *select* versions of Python to 8 | use ``--jobs`` and multiprocessing 9 | 10 | - **Improvement** Update bounds on McCabe 11 | 12 | - **Improvement** Update bounds on PyFlakes and blacklist known broken 13 | versions 14 | 15 | - **Improvement** Handle new PyFlakes warning with a new error code: F405 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.6.1.rst: -------------------------------------------------------------------------------- 1 | 2.6.1 - 2016-06-25 2 | ------------------ 3 | 4 | - **Bug** Update the config files to search for to include ``setup.cfg`` and 5 | ``tox.ini``. This was broken in 2.5.5 when we stopped passing 6 | ``config_file`` to our Style Guide 7 | -------------------------------------------------------------------------------- /docs/source/release-notes/2.6.2.rst: -------------------------------------------------------------------------------- 1 | 2.6.2 - 2016-06-25 2 | ------------------ 3 | 4 | - **Bug** Fix packaging error during release process. 5 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.0.0.rst: -------------------------------------------------------------------------------- 1 | 3.0.0 -- 2016-07-25 2 | ------------------- 3 | 4 | - Rewrite our documentation from scratch! (https://flake8.pycqa.org) 5 | 6 | - Drop explicit support for Pythons 2.6, 3.2, and 3.3. 7 | 8 | - Remove dependence on pep8/pycodestyle for file processing, plugin 9 | dispatching, and more. We now control all of this while keeping backwards 10 | compatibility. 11 | 12 | - ``--select`` and ``--ignore`` can now both be specified and try to find the 13 | most specific rule from each. For example, if you do ``--select E --ignore 14 | E123`` then we will report everything that starts with ``E`` except for 15 | ``E123``. Previously, you would have had to do ``--ignore E123,F,W`` which 16 | will also still work, but the former should be far more intuitive. 17 | 18 | - Add support for in-line ``# noqa`` comments to specify **only** the error 19 | codes to be ignored, e.g., ``# noqa: E123,W503`` 20 | 21 | - Add entry-point for formatters as well as a base class that new formatters 22 | can inherit from. See the documentation for more details. 23 | 24 | - Add detailed verbose output using the standard library logging module. 25 | 26 | - Enhance our usage of optparse for plugin developers by adding new parameters 27 | to the ``add_option`` that plugins use to register new options. 28 | 29 | - Update ``--install-hook`` to require the name of version control system hook 30 | you wish to install a Flake8. 31 | 32 | - Stop checking sub-directories more than once via the setuptools command 33 | 34 | - When passing a file on standard-in, allow the caller to specify 35 | ``--stdin-display-name`` so the output is properly formatted 36 | 37 | - The Git hook now uses ``sys.executable`` to format the shebang line. 38 | This allows Flake8 to install a hook script from a virtualenv that points to 39 | that virtualenv's Flake8 as opposed to a global one (without the virtualenv 40 | being sourced). 41 | 42 | - Print results in a deterministic and consistent ordering when used with 43 | multiprocessing 44 | 45 | - When using ``--count``, the output is no longer written to stderr. 46 | 47 | - AST plugins can either be functions or classes and all plugins can now 48 | register options so long as there are callable attributes named as we 49 | expect. 50 | 51 | - Stop forcibly re-adding ``.tox``, ``.eggs``, and ``*.eggs`` to 52 | ``--exclude``. Flake8 2.x started always appending those three patterns 53 | to any exclude list (including the default and any user supplied list). 54 | Flake8 3 has stopped adding these in, so you may see errors when upgrading 55 | due to these patterns no longer being forcibly excluded by default if you 56 | have your own exclude patterns specified. 57 | 58 | To fix this, add the appropriate patterns to your exclude patterns list. 59 | 60 | .. note:: 61 | 62 | This item was added in November of 2016, as a result of a bug 63 | report. 64 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.0.1.rst: -------------------------------------------------------------------------------- 1 | 3.0.1 -- 2016-07-25 2 | ------------------- 3 | 4 | - Fix regression in handling of ``# noqa`` for multiline strings. 5 | (See also :issue:`1024`) 6 | 7 | - Fix regression in handling of ``--output-file`` when not also using 8 | ``--verbose``. (See also :issue:`1026`) 9 | 10 | - Fix regression in handling of ``--quiet``. (See also :issue:`1026`) 11 | 12 | - Fix regression in handling of ``--statistics``. (See also :issue:`1026`) 13 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.0.2.rst: -------------------------------------------------------------------------------- 1 | 3.0.2 -- 2016-07-26 2 | ------------------- 3 | 4 | - Fix local config file discovery. (See also :issue:`528`) 5 | 6 | - Fix indexing of column numbers. We accidentally were starting column indices 7 | at 0 instead of 1. 8 | 9 | - Fix regression in handling of errors like E402 that rely on a combination of 10 | attributes. (See also :issue:`530`) 11 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.0.3.rst: -------------------------------------------------------------------------------- 1 | 3.0.3 -- 2016-07-30 2 | ------------------- 3 | 4 | - Disable ``--jobs`` for any version of Python on Windows. 5 | (See also `this Python bug report`_) 6 | 7 | - Raise exception when entry_point in plugin not callable. 8 | This raises an informative error when a plugin fails to load because its 9 | entry_point is not callable, which can happen with a plugin which is buggy or 10 | not updated for the current version of flake8. This is nicer than raising a 11 | `PicklingError` about failing to pickle a module (See also :issue:`1014`) 12 | 13 | - Fix ``# noqa`` comments followed by a ``:`` and explanation broken by 14 | 3.0.0 (See also :issue:`1025`) 15 | 16 | - Always open our output file in append mode so we do not overwrite log 17 | messages. (See also :issue:`535`) 18 | 19 | - When normalizing path values read from configuration, keep in context the 20 | directory where the configuration was found so that relative paths work. 21 | (See also :issue:`1036`) 22 | 23 | - Fix issue where users were unable to ignore plugin errors that were on 24 | by default. (See also :issue:`1037`) 25 | 26 | - Fix our legacy API StyleGuide's ``init_report`` method to actually override 27 | the previous formatter. (See also :issue:`136`) 28 | 29 | 30 | .. links 31 | .. _this Python bug report: 32 | https://bugs.python.org/issue27649 33 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.0.4.rst: -------------------------------------------------------------------------------- 1 | 3.0.4 -- 2016-08-08 2 | ------------------- 3 | 4 | - Side-step a Pickling Error when using Flake8 with multiprocessing on Unix 5 | systems. (See also :issue:`1014`) 6 | 7 | - Fix an Attribute Error raised when dealing with Invalid Syntax. (See also 8 | :issue:`539`) 9 | 10 | - Fix an unhandled Syntax Error when tokenizing files. (See also 11 | :issue:`540`) 12 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.1.0.rst: -------------------------------------------------------------------------------- 1 | 3.1.0 -- 2016-11-14 2 | ------------------- 3 | 4 | You can view the `3.1.0 milestone`_ on GitHub for more details. 5 | 6 | - Add ``--bug-report`` flag to make issue reporters' lives easier. 7 | 8 | - Collect configuration files from the current directory when using our Git 9 | hook. (See also :issue:`142`, :issue:`150`, :issue:`155`) 10 | 11 | - Avoid unhandled exceptions when dealing with SyntaxErrors. (See also 12 | :issue:`146`, :issue:`170`) 13 | 14 | - Exit early if the value for ``--diff`` is empty. (See also :issue:`158`) 15 | 16 | - Handle empty ``--stdin-display-name`` values. (See also :issue:`167`) 17 | 18 | - Properly report the column number of Syntax Errors. We were assuming that 19 | all reports of column numbers were 0-indexed, however, SyntaxErrors report 20 | the column number as 1-indexed. This caused us to report a column number 21 | that was 1 past the actual position. Further, when combined with 22 | SyntaxErrors that occur at a newline, this caused the position to be 23 | visually off by two. (See also :issue:`169`) 24 | 25 | - Fix the behaviour of ``--enable-extensions``. Previously, items specified 26 | here were still ignored due to the fact that the off-by-default extension 27 | codes were being left in the ``ignore`` list. (See also :issue:`171`) 28 | 29 | - Fix problems around ``--select`` and ``--ignore`` behaviour that prevented 30 | codes that were neither explicitly selected nor explicitly ignored from 31 | being reported. (See also :issue:`174`) 32 | 33 | - Truly be quiet when the user specifies ``-q`` one or more times. Previously, 34 | we were showing the if the user specified ``-q`` and ``--show-source``. We 35 | have fixed this bug. (See also :issue:`177`) 36 | 37 | - Add new File Processor attribute, ``previous_unindented_logical_line`` to 38 | accommodate pycodestyle 2.1.0. (See also :issue:`178`) 39 | 40 | - When something goes wrong, exit non-zero. (See also :issue:`180`, 41 | :issue:`141`) 42 | 43 | - Add ``--tee`` as an option to allow use of ``--output-file`` and printing to 44 | standard out. 45 | 46 | - Allow the git plugin to actually be lazy when collecting files. 47 | 48 | - Allow for pycodestyle 2.1 series and pyflakes 1.3 series. 49 | 50 | .. links 51 | .. _3.1.0 milestone: 52 | https://github.com/pycqa/flake8/milestone/12 53 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.1.1.rst: -------------------------------------------------------------------------------- 1 | 3.1.1 -- 2016-11-14 2 | ------------------- 3 | 4 | You can view the `3.1.1 milestone`_ on GitHub for more details. 5 | 6 | - Do not attempt to install/distribute a ``man`` file with the Python package; 7 | leave this for others to do. (See also :issue:`186`) 8 | 9 | - Fix packaging bug where wheel version constraints specified in setup.cfg did 10 | not match the constraints in setup.py. (See also :issue:`187`) 11 | 12 | .. links 13 | .. _3.1.1 milestone: 14 | https://github.com/pycqa/flake8/milestone/13 15 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.2.0.rst: -------------------------------------------------------------------------------- 1 | 3.2.0 -- 2016-11-14 2 | ------------------- 3 | 4 | You can view the `3.2.0 milestone`_ on GitHub for more details. 5 | 6 | - Allow for pycodestyle 2.2.0 which fixes a bug in E305 (See also 7 | :issue:`188`) 8 | 9 | .. links 10 | .. _3.2.0 milestone: 11 | https://github.com/pycqa/flake8/milestone/14 12 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.2.1.rst: -------------------------------------------------------------------------------- 1 | 3.2.1 -- 2016-11-21 2 | ------------------- 3 | 4 | You can view the `3.2.1 milestone`_ on GitHub for more details. 5 | 6 | - Fix subtle bug when deciding whether to report an on-by-default's violation 7 | (See also :issue:`189`) 8 | 9 | - Fix another bug around SyntaxErrors not being reported at the right column 10 | and row (See also :issue:`191` and :issue:`169` for a related, previously 11 | fixed bug) 12 | 13 | - Fix regression from 2.x where we run checks against explicitly provided 14 | files, even if they don't match the filename patterns. (See also 15 | :issue:`198`) 16 | 17 | .. links 18 | .. _3.2.1 milestone: 19 | https://github.com/pycqa/flake8/milestone/15 20 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.3.0.rst: -------------------------------------------------------------------------------- 1 | 3.3.0 -- 2017-02-06 2 | ------------------- 3 | 4 | You can view the `3.3.0 milestone`_ on GitHub for more details. 5 | 6 | - Add support for Python 3.6 (via dependencies). **Note** Flake8 does not 7 | guarantee that all plugins will support Python 3.6. 8 | 9 | - Added unique error codes for all missing PyFlakes messages. (14 new 10 | codes, see "Error / Violation Codes") 11 | 12 | - Dramatically improve the performance of Flake8. (See also :issue:`829`) 13 | 14 | - Display the local file path instead of the temporary file path when 15 | using the git hook. (See also :issue:`176`) 16 | 17 | - Add methods to Report class that will be called when Flake8 starts and 18 | finishes processing a file. (See also :issue:`183`) 19 | 20 | - Fix problem where hooks should only check \*.py files. (See also 21 | :issue:`200`) 22 | 23 | - Fix handling of SyntaxErrors that do not include physical line information. 24 | (See also :issue:`542`) 25 | 26 | - Update upper bound on PyFlakes to allow for PyFlakes 1.5.0. (See also 27 | :issue:`549`) 28 | 29 | - Update setuptools integration to less eagerly deduplicate packages. 30 | (See also :issue:`552`) 31 | 32 | - Force ``flake8 --version`` to be repeatable between invocations. (See also 33 | :issue:`554`) 34 | 35 | .. all links 36 | .. _3.3.0 milestone: 37 | https://github.com/pycqa/flake8/milestone/16 38 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.4.0.rst: -------------------------------------------------------------------------------- 1 | 3.4.0 -- 2017-07-27 2 | ------------------- 3 | 4 | You can view the `3.4.0 milestone`_ on GitHub for more details. 5 | 6 | - Refine logic around ``--select`` and ``--ignore`` when combined with the 7 | default values for each. (See also :issue:`572`) 8 | 9 | - Handle spaces as an alternate separate for error codes, e.g., 10 | ``--ignore 'E123 E234'``. (See also :issue:`580`) 11 | 12 | - Filter out empty select and ignore codes, e.g., ``--ignore E123,,E234``. 13 | (See also :issue:`581`) 14 | 15 | - Specify dependencies appropriately in ``setup.py`` (See also :issue:`592`) 16 | 17 | - Fix bug in parsing ``--quiet`` and ``--verbose`` from config files. 18 | (See also :issue:`1169`) 19 | 20 | - Remove unused import of ``os`` in the git hook template (See also 21 | :issue:`1170`) 22 | 23 | .. all links 24 | .. _3.4.0 milestone: 25 | https://github.com/pycqa/flake8/milestone/17 26 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.4.1.rst: -------------------------------------------------------------------------------- 1 | 3.4.1 -- 2017-07-28 2 | ------------------- 3 | 4 | You can view the `3.4.1 milestone`_ on GitHub for more details. 5 | 6 | - Fix minor regression when users specify only a ``--select`` list with items 7 | in the enabled/extended select list. (See also :issue:`605`) 8 | 9 | .. all links 10 | .. _3.4.1 milestone: 11 | https://github.com/pycqa/flake8/milestone/18 12 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.5.0.rst: -------------------------------------------------------------------------------- 1 | 3.5.0 -- 2017-10-23 2 | ------------------- 3 | 4 | You can view the `3.5.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Allow for PyFlakes 1.6.0 (See also :issue:`1058`) 10 | 11 | - Start using new PyCodestyle checks for bare excepts and ambiguous identifier 12 | (See also :issue:`611`) 13 | 14 | Features 15 | ~~~~~~~~ 16 | 17 | - Print out information about configuring VCS hooks (See also :issue:`586`) 18 | 19 | - Allow users to develop plugins "local" to a repository without using 20 | setuptools. See our documentation on local plugins for more information. 21 | (See also :issue:`608`) 22 | 23 | Bugs Fixed 24 | ~~~~~~~~~~ 25 | 26 | - Catch and helpfully report ``UnicodeDecodeError``\ s when parsing 27 | configuration files. (See also :issue:`609`) 28 | 29 | 30 | .. all links 31 | .. _3.5.0 milestone: 32 | https://github.com/pycqa/flake8/milestone/19 33 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.6.0.rst: -------------------------------------------------------------------------------- 1 | 3.6.0 -- 2018-10-23 2 | ------------------- 3 | 4 | You can view the `3.6.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - pycodestyle has been updated to >= 2.4.0, < 2.5.0 (See also :issue:`1068`, 10 | :issue:`652`, :issue:`869`, :issue:`881`, :issue:`1239`) 11 | 12 | - Pyflakes has been updated to >= 2.0.0, < 2.1.0 (See also :issue:`655`, 13 | :issue:`883`) 14 | 15 | - flake8 requires python 2.x >= 2.7 or python 3.x >= 3.4 (See also 16 | :issue:`876`) 17 | 18 | Features 19 | ~~~~~~~~ 20 | 21 | - Add ``paths`` to allow local plugins to exist outside of ``sys.path`` (See 22 | also :issue:`1067`, :issue:`1237`) 23 | 24 | - Copy ``setup.cfg`` files to the temporary git hook execution directory (See 25 | also :issue:`1299`) 26 | 27 | - Only skip a file if ``# flake8: noqa`` is on a line by itself (See also 28 | :issue:`259`, :issue:`873`) 29 | 30 | - Provide a better user experience for broken plugins (See also :issue:`1178`) 31 | 32 | - Report ``E902`` when a file passed on the command line does not exist (See 33 | also :issue:`645`, :issue:`878`) 34 | 35 | - Add ``--extend-ignore`` for extending the default ``ignore`` instead of 36 | overriding it (See also :issue:`1061`, :issue:`1180`) 37 | 38 | Bugs Fixed 39 | ~~~~~~~~~~ 40 | 41 | - Respect a formatter's newline setting when printing (See also :issue:`1238`) 42 | 43 | - Fix leaking of processes in the legacy api (See also :issue:`650`, 44 | :issue:`879`) 45 | 46 | - Fix a ``SyntaxWarning`` for an invalid escape sequence (See also 47 | :issue:`1186`) 48 | 49 | - Fix ``DeprecationWarning`` due to import of ``abc`` classes from the 50 | ``collections`` module (See also :issue:`887`) 51 | 52 | - Defer ``setuptools`` import to improve flake8 startup time (See also 53 | :issue:`1190`) 54 | 55 | - Fix inconsistent line endings in ``FileProcessor.lines`` when running under 56 | python 3.x (See also :issue:`263`, :issue:`889`) 57 | 58 | 59 | .. all links 60 | .. _3.6.0 milestone: 61 | https://github.com/pycqa/flake8/milestone/20 62 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.0.rst: -------------------------------------------------------------------------------- 1 | 3.7.0 -- 2019-01-29 2 | ------------------- 3 | 4 | You can view the `3.7.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Add dependency on ``entrypoints`` >= 0.3, < 0.4 (See also :issue:`897`, 10 | :issue:`1197`) 11 | 12 | - Pyflakes has been updated to >= 2.1.0, < 2.2.0 (See also :issue:`912`, 13 | :issue:`913`) 14 | 15 | - pycodestyle has been updated to >= 2.5.0, < 2.6.0 (See also :issue:`915`) 16 | 17 | Features 18 | ~~~~~~~~ 19 | 20 | - Add support for ``per-file-ignores`` (See also :issue:`892`, :issue:`511`, 21 | :issue:`911`, :issue:`277`) 22 | 23 | - Enable use of ``float`` and ``complex`` option types (See also :issue:`894`, 24 | :issue:`258`) 25 | 26 | - Improve startup performance by switching from ``pkg_resources`` to 27 | ``entrypoints`` (See also :issue:`897`) 28 | 29 | - Add metadata for use through the `pre-commit`_ git hooks framework (See also 30 | :issue:`901`, :issue:`1196`) 31 | 32 | - Allow physical line checks to return more than one result (See also 33 | :issue:`902`) 34 | 35 | - Allow ``# noqa:X123`` comments without space between the colon and codes 36 | list (See also :issue:`906`, :issue:`276`) 37 | 38 | - Remove broken and unused ``flake8.listen`` plugin type (See also 39 | :issue:`907`, :issue:`663`) 40 | 41 | .. all links 42 | .. _3.7.0 milestone: 43 | https://github.com/pycqa/flake8/milestone/22 44 | .. _pre-commit: 45 | https://pre-commit.com/ 46 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.1.rst: -------------------------------------------------------------------------------- 1 | 3.7.1 -- 2019-01-30 2 | ------------------- 3 | 4 | You can view the `3.7.1 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix capitalized filenames in ``per-file-ignores`` setting (See also 10 | :issue:`917`, :issue:`287`) 11 | 12 | .. all links 13 | .. _3.7.1 milestone: 14 | https://github.com/pycqa/flake8/milestone/23 15 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.2.rst: -------------------------------------------------------------------------------- 1 | 3.7.2 -- 2019-01-30 2 | ------------------- 3 | 4 | You can view the `3.7.2 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix broken ``flake8 --diff`` (regressed in 3.7.0) (See also :issue:`919`, 10 | :issue:`667`) 11 | 12 | - Fix typo in plugin exception reporting (See also :issue:`908`, 13 | :issue:`668`) 14 | 15 | - Fix ``AttributeError`` while attempting to use the legacy api (regressed in 16 | 3.7.0) (See also :issue:`1198`, :issue:`673`) 17 | 18 | .. all links 19 | .. _3.7.2 milestone: 20 | https://github.com/pycqa/flake8/milestone/24 21 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.3.rst: -------------------------------------------------------------------------------- 1 | 3.7.3 -- 2019-01-30 2 | ------------------- 3 | 4 | You can view the `3.7.3 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix imports of ``typing`` in python 3.5.0 / 3.5.1 (See also :issue:`1199`, 10 | :issue:`674`) 11 | 12 | - Fix ``flake8 --statistics`` (See also :issue:`920`, :issue:`675`) 13 | 14 | - Gracefully ignore ``flake8-per-file-ignores`` plugin if installed (See also 15 | :issue:`1201`, :issue:`671`) 16 | 17 | - Improve error message for malformed ``per-file-ignores`` (See also 18 | :issue:`921`, :issue:`288`) 19 | 20 | 21 | .. all links 22 | .. _3.7.3 milestone: 23 | https://github.com/pycqa/flake8/milestone/25 24 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.4.rst: -------------------------------------------------------------------------------- 1 | 3.7.4 -- 2019-01-31 2 | ------------------- 3 | 4 | You can view the `3.7.4 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix performance regression with lots of ``per-file-ignores`` and errors 10 | (See also :issue:`922`, :issue:`677`) 11 | 12 | 13 | .. all links 14 | .. _3.7.4 milestone: 15 | https://github.com/pycqa/flake8/milestone/26 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.5.rst: -------------------------------------------------------------------------------- 1 | 3.7.5 -- 2019-02-04 2 | ------------------- 3 | 4 | You can view the `3.7.5 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix reporting of pyflakes "referenced before assignment" error (See also 10 | :issue:`923`, :issue:`679`) 11 | 12 | 13 | .. all links 14 | .. _3.7.5 milestone: 15 | https://github.com/pycqa/flake8/milestone/27 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.6.rst: -------------------------------------------------------------------------------- 1 | 3.7.6 -- 2019-02-18 2 | ------------------- 3 | 4 | You can view the `3.7.6 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix ``--per-file-ignores`` for multi-letter error codes (See also 10 | :issue:`1203`, :issue:`683`) 11 | 12 | - Improve flake8 speed when only 1 filename is passed (See also :issue:`1204`) 13 | 14 | 15 | .. all links 16 | .. _3.7.6 milestone: 17 | https://github.com/pycqa/flake8/milestone/28 18 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.7.rst: -------------------------------------------------------------------------------- 1 | 3.7.7 -- 2019-02-25 2 | ------------------- 3 | 4 | You can view the `3.7.7 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix crashes in plugins causing ``flake8`` to hang while unpickling errors 10 | (See also :issue:`1206`, :issue:`681`) 11 | 12 | 13 | .. all links 14 | .. _3.7.7 milestone: 15 | https://github.com/pycqa/flake8/milestone/29 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.8.rst: -------------------------------------------------------------------------------- 1 | 3.7.8 -- 2019-07-08 2 | ------------------- 3 | 4 | You can view the `3.7.8 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix handling of ``Application.parse_preliminary_options_and_args`` when 10 | argv is an empty list (See also :issue:`1303`, :issue:`694`) 11 | 12 | - Fix crash when a file parses but fails to tokenize (See also :issue:`1210`, 13 | :issue:`1088`) 14 | 15 | - Log the full traceback on plugin exceptions (See also :issue:`926`) 16 | 17 | - Fix ``# noqa: ...`` comments with multi-letter codes (See also :issue:`931`, 18 | :issue:`1101`) 19 | 20 | 21 | .. all links 22 | .. _3.7.8 milestone: 23 | https://github.com/pycqa/flake8/milestone/30 24 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.7.9.rst: -------------------------------------------------------------------------------- 1 | 3.7.9 -- 2019-10-28 2 | ------------------- 3 | 4 | You can view the `3.7.9 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Disable multiprocessing when the multiprocessing method is ``spawn`` (such 10 | as on macos in python3.8) (See also :issue:`956`, :issue:`315`) 11 | 12 | 13 | .. all links 14 | .. _3.7.9 milestone: 15 | https://github.com/pycqa/flake8/milestone/32 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.8.0.rst: -------------------------------------------------------------------------------- 1 | 3.8.0 -- 2020-05-11 2 | ------------------- 3 | 4 | You can view the `3.8.0 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix logical checks which report positions out of bounds (See also 10 | :issue:`987`, :issue:`723`) 11 | 12 | - Fix ``--exclude=.*`` accidentally matching ``.`` and ``..`` (See also 13 | :issue:`441`, :issue:`360`) 14 | 15 | Deprecations 16 | ~~~~~~~~~~~~ 17 | 18 | - Add deprecation message for vcs hooks (See also :issue:`985`, 19 | :issue:`296`) 20 | 21 | 22 | 3.8.0a2 -- 2020-04-24 23 | --------------------- 24 | 25 | You can view the `3.8.0 milestone`_ on GitHub for more details. 26 | 27 | Bugs Fixed 28 | ~~~~~~~~~~ 29 | 30 | - Fix ``type="str"`` optparse options (See also :issue:`984`) 31 | 32 | 33 | 3.8.0a1 -- 2020-04-24 34 | --------------------- 35 | 36 | You can view the `3.8.0 milestone`_ on GitHub for more details. 37 | 38 | New Dependency Information 39 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 40 | 41 | - Remove dependency on ``entrypoints`` and add dependency on 42 | ``importlib-metadata`` (only for ``python<3.8``) (See also :issue:`1297`, 43 | :issue:`297`) 44 | 45 | - Pyflakes has been updated to >= 2.2.0, < 2.3.0 (See also :issue:`982`) 46 | 47 | - pycodestyle has been updated to >= 2.6.0a1, < 2.7.0 (See also :issue:`983`) 48 | 49 | Features 50 | ~~~~~~~~ 51 | 52 | - Add ``--extend-exclude`` option to add to ``--exclude`` without overwriting 53 | (See also :issue:`1211`, :issue:`1091`) 54 | 55 | - Move argument parsing from ``optparse`` to ``argparse`` (See also 56 | :issue:`939` 57 | 58 | - Group plugin options in ``--help`` (See also :issue:`1219`, :issue:`294`) 59 | 60 | - Remove parsing of ``verbose`` from configuration files as it was not 61 | consistently applied (See also :issue:`1245`, :issue:`245`) 62 | 63 | - Remove parsing of ``output_file`` from configuration files as it was not 64 | consistently applied (See also :issue:`1246`) 65 | 66 | - Resolve configuration files relative to ``cwd`` instead of common prefix of 67 | passed filenames. You may need to change ``flake8 subproject`` to 68 | ``cd subproject && flake8 .`` (See also :issue:`952`) 69 | 70 | - Officially support python3.8 (See also :issue:`963`) 71 | 72 | - ``--disable-noqa`` now also disables ``# flake8: noqa`` (See also 73 | :issue:`1296`, :issue:`318`) 74 | 75 | - Ensure that a missing file produces a ``E902`` error (See also :issue:`1262`, 76 | :issue:`328`) 77 | 78 | - ``# noqa`` comments now apply to all of the lines in an explicit ``\`` 79 | continuation or in a line continued by a multi-line string (See also 80 | :issue:`1266`, :issue:`621`) 81 | 82 | Bugs Fixed 83 | ~~~~~~~~~~ 84 | 85 | - Fix ``--exclude=./t.py`` to only match ``t.py`` at the top level (See also 86 | :issue:`1208`, :issue:`628`) 87 | 88 | - Fix ``--show-source`` when a file is indented with tabs (See also 89 | :issue:`1218`, :issue:`719`) 90 | 91 | - Fix crash when ``--max-line-length`` is given a non-integer (See also 92 | :issue:`939`, :issue:`704`) 93 | 94 | - Prevent flip-flopping of ``indent_char`` causing extra ``E101`` errors (See 95 | also :issue:`949`, `pycodestyle#886`_) 96 | 97 | - Only enable multiprocessing when the method is ``fork`` fixing issues 98 | on macos with python3.8+ (See also :issue:`955`, :issue:`315`) (note: this 99 | fix also landed in 3.7.9) 100 | 101 | - ``noqa`` is now only handled by flake8 fixing specific-noqa. Plugins 102 | requesting this parameter will always receive ``False`` (See also 103 | :issue:`1214`, :issue:`1104`) 104 | 105 | - Fix duplicate loading of plugins when invoked via ``python -m flake8`` (See 106 | also :issue:`1297`) 107 | 108 | - Fix early exit when ``--exit-zero`` and ``--diff`` are provided and the diff 109 | is empty (See also :issue:`970`) 110 | 111 | - Consistently split lines when ``\f`` is present when reading from stdin (See 112 | also :issue:`976`, :issue:`202`) 113 | 114 | Deprecations 115 | ~~~~~~~~~~~~ 116 | 117 | - ``python setup.py flake8`` (setuptools integration) is now deprecated and 118 | will be removed in a future version (See also :issue:`935`, :issue:`1098`) 119 | 120 | - ``type='string'`` (optparse) types are deprecated, use 121 | ``type=callable`` (argparse) instead. Support for ``type='string'`` will 122 | be removed in a future version (See also :issue:`939`) 123 | 124 | - ``%default`` in plugin option help text is deprecated, use ``%(default)s`` 125 | instead. Support for ``%default`` will be removed in a future version (See 126 | also :issue:`939`) 127 | 128 | - optparse-style ``action='callback'`` setting for options is deprecated, use 129 | argparse action classes instead. This will be removed in a future version 130 | (See also :issue:`939`) 131 | 132 | 133 | .. all links 134 | .. _3.8.0 milestone: 135 | https://github.com/pycqa/flake8/milestone/31 136 | 137 | .. issue links 138 | .. _pycodestyle#886: 139 | https://github.com/PyCQA/pycodestyle/issues/886 140 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.8.1.rst: -------------------------------------------------------------------------------- 1 | 3.8.1 -- 2020-05-11 2 | ------------------- 3 | 4 | You can view the `3.8.1 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix ``--output-file`` (regression in 3.8.0) (See also :issue:`990`, 10 | :issue:`725`) 11 | 12 | 13 | .. all links 14 | .. _3.8.1 milestone: 15 | https://github.com/pycqa/flake8/milestone/33 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.8.2.rst: -------------------------------------------------------------------------------- 1 | 3.8.2 -- 2020-05-22 2 | ------------------- 3 | 4 | You can view the `3.8.2 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Improve performance by eliminating unnecessary sort (See also :issue:`991`) 10 | 11 | - Improve messaging of ``--jobs`` argument by utilizing ``argparse`` (See also 12 | :issue:`1269`, :issue:`1110`) 13 | 14 | - Fix file configuration options to be relative to the config passed on the 15 | command line (See also :issue:`442`, :issue:`736`) 16 | 17 | - Fix incorrect handling of ``--extend-exclude`` by treating its values as 18 | files (See also :issue:`1271`, :issue:`738`) 19 | 20 | .. all links 21 | .. _3.8.2 milestone: 22 | https://github.com/pycqa/flake8/milestone/34 23 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.8.3.rst: -------------------------------------------------------------------------------- 1 | 3.8.3 -- 2020-06-08 2 | ------------------- 3 | 4 | You can view the `3.8.3 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Also catch ``SyntaxError`` when tokenizing (See also :issue:`992`, 10 | :issue:`747`) 11 | 12 | - Fix ``--jobs`` default display in ``flake8 --help`` (See also :issue:`1272`, 13 | :issue:`750`) 14 | 15 | .. all links 16 | .. _3.8.3 milestone: 17 | https://github.com/pycqa/flake8/milestone/35 18 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.8.4.rst: -------------------------------------------------------------------------------- 1 | 3.8.4 -- 2020-10-02 2 | ------------------- 3 | 4 | You can view the `3.8.4 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix multiprocessing errors on platforms without ``sem_open`` syscall. (See 10 | also :issue:`1282`) 11 | 12 | - Fix skipping of physical checks on the last line of a file which does not 13 | end in a newline (See also :issue:`997`) 14 | 15 | .. all links 16 | .. _3.8.4 milestone: 17 | https://github.com/pycqa/flake8/milestone/36 18 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.9.0.rst: -------------------------------------------------------------------------------- 1 | 3.9.0 -- 2021-03-14 2 | ------------------- 3 | 4 | You can view the `3.9.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Pyflakes has been updated to >= 2.3.0, < 2.4.0 (See also :issue:`1006`) 10 | 11 | - pycodestyle has been updated to >= 2.7.0, < 2.8.0 (See also :issue:`1007`) 12 | 13 | Deprecations 14 | ~~~~~~~~~~~~ 15 | 16 | - Drop support for python 3.4 (See also :issue:`1283`) 17 | 18 | Features 19 | ~~~~~~~~ 20 | 21 | - Add ``--no-show-source`` option to disable ``--show-source`` (See also 22 | :issue:`995`) 23 | 24 | Bugs Fixed 25 | ~~~~~~~~~~ 26 | 27 | - Fix handling of ``crlf`` line endings when linting stdin (See also 28 | :issue:`1002`) 29 | 30 | 31 | .. all links 32 | .. _3.9.0 milestone: 33 | https://github.com/pycqa/flake8/milestone/37 34 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.9.1.rst: -------------------------------------------------------------------------------- 1 | 3.9.1 -- 2021-04-15 2 | ------------------- 3 | 4 | You can view the `3.9.1 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix codes being ignored by plugins utilizing ``extend_default_ignore`` (See 10 | also :pull:`1317`) 11 | 12 | 13 | .. all links 14 | .. _3.9.1 milestone: 15 | https://github.com/PyCQA/flake8/milestone/38 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/3.9.2.rst: -------------------------------------------------------------------------------- 1 | 3.9.2 -- 2021-05-08 2 | ------------------- 3 | 4 | You can view the `3.9.2 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix error message for ``E111`` in ``pycodestyle`` (See also :pull:`1328`, 10 | :issue:`1327`). 11 | 12 | Deprecations 13 | ~~~~~~~~~~~~ 14 | 15 | - ``indent_size_str`` is deprecated, use ``str(indent_size)`` instead (See 16 | also :pull:`1328`, :issue:`1327`). 17 | 18 | 19 | .. all links 20 | .. _3.9.2 milestone: 21 | https://github.com/PyCQA/flake8/milestone/40 22 | -------------------------------------------------------------------------------- /docs/source/release-notes/4.0.0.rst: -------------------------------------------------------------------------------- 1 | 4.0.0 -- 2021-10-10 2 | ------------------- 3 | 4 | You can view the `4.0.0 milestone`_ on GitHub for more details. 5 | 6 | Backwards Incompatible Changes 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Remove ``--install-hook`` vcs integration (See also :issue:`1008`). 10 | - Remove ``setuptools`` command (See also :issue:`1009`). 11 | - Migrate from GitLab to GitHub (See also :pull:`1305`). 12 | - Due to constant confusion by users, user-level |Flake8| configuration files 13 | are no longer supported. Files will not be searched for in the user's home 14 | directory (e.g., ``~/.flake8``) nor in the XDG config directory (e.g., 15 | ``~/.config/flake8``). (See also :pull:`1404`). 16 | 17 | New Dependency Information 18 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | 20 | - pycodestyle has been updated to >= 2.8.0, < 2.9.0 (See also :pull:`1406`). 21 | - Pyflakes has been updated to >= 2.4.0, < 2.5.0 (See also :pull:`1406`). 22 | - flake8 requires python >= 3.6 (See also :issue:`1010`). 23 | 24 | Features 25 | ~~~~~~~~ 26 | 27 | - Add ``--extend-select`` option (See also :pull:`1312` :issue:`1061`). 28 | - Automatically create directories for output files (See also :pull:`1329`). 29 | 30 | Bugs Fixed 31 | ~~~~~~~~~~ 32 | 33 | - ``ast`` parse before tokenizing to improve ``SyntaxError`` errors (See also 34 | :pull:`1320` :issue:`740`). 35 | - Fix warning in ``--indent-size`` argparse help (See also :pull:`1367`). 36 | - Fix handling ``SyntaxError`` in python 3.10+ (See also :pull:`1374` 37 | :issue:`1372`). 38 | - Fix writing non-cp1252-encodable when output is piped on windows (See also 39 | :pull:`1382` :issue:`1381`). 40 | 41 | .. all links 42 | .. _4.0.0 milestone: 43 | https://github.com/PyCQA/flake8/milestone/39 44 | -------------------------------------------------------------------------------- /docs/source/release-notes/4.0.1.rst: -------------------------------------------------------------------------------- 1 | 4.0.1 -- 2021-10-11 2 | ------------------- 3 | 4 | You can view the `4.0.1 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix parallel execution collecting a ``SyntaxError`` (See also :pull:`1410` 10 | :issue:`1408`). 11 | 12 | 13 | .. all links 14 | .. _4.0.1 milestone: 15 | https://github.com/PyCQA/flake8/milestone/41 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/5.0.0.rst: -------------------------------------------------------------------------------- 1 | 5.0.0 -- 2022-07-30 2 | ------------------- 3 | 4 | You can view the `5.0.0 milestone`_ on GitHub for more details. 5 | 6 | Backwards Incompatible Changes 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Remove ``indent_size_str`` (See also :pull:`1411`). 10 | - Remove some dead code (See also :pull:`1453`, :pull:`1540`, :pull:`1541`). 11 | - Missing explicitly-specified configuration is now an error (See also 12 | :issue:`1497`, :pull:`1498`). 13 | - Always read configuration files as UTF-8 (See also :issue:`1532`, 14 | :pull:`1533`). 15 | - Remove manpage from docs -- use ``help2man`` or related tools instead (See 16 | also :pull:`1557`). 17 | - Forbid invalid plugin codes (See also :issue:`325`, :pull:`1579`). 18 | 19 | 20 | Deprecations 21 | ~~~~~~~~~~~~ 22 | 23 | - Deprecate ``--diff`` option (See also :issue:`1389`, :pull:`1441`). 24 | 25 | 26 | New Dependency Information 27 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 | 29 | - pycodestyle has been updated to >= 2.9.0, < 2.10.0 (See also :pull:`1626`). 30 | - Pyflakes has been updated to >= 2.5.0, < 2.6.0 (See also :pull:`1625`). 31 | - mccabe has been updated to >= 0.7.0, < 0.8.0 (See also :pull:`1542`). 32 | 33 | 34 | Features 35 | ~~~~~~~~ 36 | 37 | - Add colors to output, configurable via ``--color`` (See also :issue:`1379`, 38 | :pull:`1440`). 39 | - Add ``.nox`` to the default exclude list (See also :issue:`1442`, 40 | :pull:`1443`). 41 | - Don't consider a config file which does not contain flake8 settings (See 42 | also :issue:`199`, :pull:`1472`). 43 | - Duplicate ``local-plugins`` names are now allowed (See also :issue:`362`, 44 | :pull:`1504`). 45 | - Consider ``.`` to be a path in config files (See also :issue:`1494`, 46 | :pull:`1508`) 47 | - Add ``--require-plugins`` option taking distribution names (See also 48 | :issue:`283`, :pull:`1535`). 49 | - Improve performance by removing debug logs (See also :pull:`1537`, 50 | :pull:`1544`). 51 | - Include failing file path in plugin execution error (See also :issue:`265`, 52 | :pull:`1543`). 53 | - Improve performance by pre-generating a ``pycodestyle`` plugin (See also 54 | :pull:`1545`). 55 | - Properly differentiate between explicitly ignored / selected and default 56 | ignored / selected options (See also :issue:`284`, :pull:`1576`, 57 | :pull:`1609`). 58 | 59 | 60 | Bugs Fixed 61 | ~~~~~~~~~~ 62 | 63 | - Fix physical line plugins not receiving all lines in the case of 64 | triple-quoted strings (See also :issue:`1534`, :pull:`1536`). 65 | - Fix duplicate error logging in the case of plugin issues (See also 66 | :pull:`1538`). 67 | - Fix inconsistent ordering of ``--ignore`` in ``--help`` (See also 68 | :issue:`1550`, :pull:`1552`). 69 | - Fix memory leak of style guides by avoiding ``@lru_cache`` of a method (See 70 | also :pull:`1573`). 71 | - Fix ignoring of configuration files exactly in the home directory (See also 72 | :issue:`1617`, :pull:`1618`). 73 | 74 | .. all links 75 | .. _5.0.0 milestone: 76 | https://github.com/PyCQA/flake8/milestone/42 77 | -------------------------------------------------------------------------------- /docs/source/release-notes/5.0.1.rst: -------------------------------------------------------------------------------- 1 | 5.0.1 -- 2022-07-31 2 | ------------------- 3 | 4 | You can view the `5.0.1 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix duplicate plugin discovery on misconfigured pythons (See also 10 | :issue:`1627`, :pull:`1631`). 11 | 12 | 13 | .. all links 14 | .. _5.0.1 milestone: 15 | https://github.com/PyCQA/flake8/milestone/43 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/5.0.2.rst: -------------------------------------------------------------------------------- 1 | 5.0.2 -- 2022-08-01 2 | ------------------- 3 | 4 | You can view the `5.0.2 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Fix execution on python == 3.8.0 (See also :issue:`1637`, :pull:`1641`). 10 | - Fix config discovery when home does not exist (See also :issue:`1640`, 11 | :pull:`1642`). 12 | 13 | 14 | .. all links 15 | .. _5.0.2 milestone: 16 | https://github.com/PyCQA/flake8/milestone/44 17 | -------------------------------------------------------------------------------- /docs/source/release-notes/5.0.3.rst: -------------------------------------------------------------------------------- 1 | 5.0.3 -- 2022-08-01 2 | ------------------- 3 | 4 | You can view the `5.0.3 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Work around partial reads of configuration files with syntax errors (See 10 | also :issue:`1647`, :pull:`1648`). 11 | 12 | 13 | .. all links 14 | .. _5.0.3 milestone: 15 | https://github.com/PyCQA/flake8/milestone/45 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/5.0.4.rst: -------------------------------------------------------------------------------- 1 | 5.0.4 -- 2022-08-03 2 | ------------------- 3 | 4 | You can view the `5.0.4 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Set a lower bound on ``importlib-metadata`` to prevent ``RecursionError`` 10 | (See also :issue:`1650`, :pull:`1653`). 11 | 12 | 13 | .. all links 14 | .. _5.0.4 milestone: 15 | https://github.com/PyCQA/flake8/milestone/46 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/6.0.0.rst: -------------------------------------------------------------------------------- 1 | 6.0.0 -- 2022-11-23 2 | ------------------- 3 | 4 | You can view the `6.0.0 milestone`_ on GitHub for more details. 5 | 6 | Backwards Incompatible Changes 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Remove ``--diff`` option (See also :issue:`1389`, :pull:`1720`). 10 | - Produce an error when invalid codes are specified in configuration (See also 11 | :issue:`1689`, :pull:`1713`). 12 | - Produce an error if the file specified in ``--extend-config`` does not exist 13 | (See also :issue:`1729`, :pull:`1732`). 14 | - Remove ``optparse`` compatibility support (See also :pull:`1739`). 15 | 16 | New Dependency Information 17 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | 19 | - pycodestyle has been updated to >= 2.10.0, < 2.11.0 (See also :pull:`1746`). 20 | - Pyflakes has been updated to >= 3.0.0, < 3.1.0 (See also :pull:`1748`). 21 | 22 | Features 23 | ~~~~~~~~ 24 | 25 | - Require python >= 3.8.1 (See also :pull:`1633`, :pull:`1741`). 26 | - List available formatters in for ``--format`` option in ``--help`` (See also 27 | :issue:`223`, :pull:`1624`). 28 | - Improve multiprocessing performance (See also :pull:`1723`). 29 | - Enable multiprocessing on non-fork platforms (See also :pull:`1723`). 30 | - Ensure results are sorted when discovered from files (See also :issue:`1670`, 31 | :pull:`1726`). 32 | 33 | .. all links 34 | .. _6.0.0 milestone: 35 | https://github.com/PyCQA/flake8/milestone/47 36 | -------------------------------------------------------------------------------- /docs/source/release-notes/6.1.0.rst: -------------------------------------------------------------------------------- 1 | 6.1.0 -- 2023-07-29 2 | ------------------- 3 | 4 | You can view the `6.1.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Pyflakes has been updated to >= 3.1.0, < 3.2.0 (See also :pull:`1847`). 10 | - pycodestyle has been updated to >= 2.11.0, < 2.12.0 (See also :pull:`1848`). 11 | 12 | Features 13 | ~~~~~~~~ 14 | 15 | - Deprecate ``--include-in-doctest``, ``--exclude-from-doctest`` (See also 16 | :issue:`1747`, :pull:`1768`). 17 | - Add support for python 3.12 (See also :pull:`1832`, :pull:`1849`, 18 | :pull:`1850`). 19 | 20 | .. all links 21 | .. _6.1.0 milestone: 22 | https://github.com/PyCQA/flake8/milestone/48 23 | -------------------------------------------------------------------------------- /docs/source/release-notes/7.0.0.rst: -------------------------------------------------------------------------------- 1 | 7.0.0 -- 2024-01-04 2 | ------------------- 3 | 4 | You can view the `7.0.0 milestone`_ on GitHub for more details. 5 | 6 | Backwards Incompatible Changes 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - Remove ``--include-in-doctest`` and ``--exclude-from-doctest`` options. 10 | (See also :issue:`1747`, :pull:`1854`) 11 | 12 | New Dependency Information 13 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | 15 | - Pyflakes has been updated to >= 3.2.0, < 3.3.0 (See also :pull:`1906`). 16 | 17 | .. all links 18 | .. _7.0.0 milestone: 19 | https://github.com/PyCQA/flake8/milestone/49 20 | -------------------------------------------------------------------------------- /docs/source/release-notes/7.1.0.rst: -------------------------------------------------------------------------------- 1 | 7.1.0 -- 2024-06-15 2 | ------------------- 3 | 4 | You can view the `7.1.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - pycodestyle has been updated to >= 2.12.0, < 2.13.0 (See also :pull:`1939`). 10 | 11 | .. all links 12 | .. _7.1.0 milestone: 13 | https://github.com/PyCQA/flake8/milestone/50 14 | -------------------------------------------------------------------------------- /docs/source/release-notes/7.1.1.rst: -------------------------------------------------------------------------------- 1 | 7.1.1 -- 2024-08-04 2 | ------------------- 3 | 4 | You can view the `7.1.1 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Properly preserve escaped `{` and `}` in fstrings in logical lines in 3.12+. 10 | (See also :issue:`1948`, :pull:`1949`). 11 | 12 | 13 | .. all links 14 | .. _7.1.1 milestone: 15 | https://github.com/PyCQA/flake8/milestone/51 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/7.1.2.rst: -------------------------------------------------------------------------------- 1 | 7.1.2 -- 2025-02-16 2 | ------------------- 3 | 4 | You can view the `7.1.2 milestone`_ on GitHub for more details. 5 | 6 | Bugs Fixed 7 | ~~~~~~~~~~ 8 | 9 | - Avoid starting unnecessary processes when "# files" < "jobs". 10 | (See also :pull:`1966`). 11 | 12 | 13 | .. all links 14 | .. _7.1.2 milestone: 15 | https://github.com/PyCQA/flake8/milestone/52 16 | -------------------------------------------------------------------------------- /docs/source/release-notes/7.2.0.rst: -------------------------------------------------------------------------------- 1 | 7.2.0 -- 2025-03-29 2 | ------------------- 3 | 4 | You can view the `7.2.0 milestone`_ on GitHub for more details. 5 | 6 | New Dependency Information 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | - pycodestyle has been updated to >= 2.13.0, < 2.14.0 (See also :pull:`1974`). 10 | - pyflakes has been updated to >= 3.3.0, < 3.4.0 (See also :pull:`1974`). 11 | 12 | Features 13 | ~~~~~~~~ 14 | 15 | - Require python >= 3.9 (See also :pull:`1973`). 16 | 17 | .. all links 18 | .. _7.2.0 milestone: 19 | https://github.com/PyCQA/flake8/milestone/53 20 | -------------------------------------------------------------------------------- /docs/source/release-notes/index.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Release Notes and History 3 | =========================== 4 | 5 | All of the release notes that have been recorded for Flake8 are organized here 6 | with the newest releases first. 7 | 8 | 7.x Release Series 9 | ================== 10 | 11 | .. toctree:: 12 | 7.0.0 13 | 7.1.0 14 | 7.1.1 15 | 7.1.2 16 | 7.2.0 17 | 18 | 6.x Release Series 19 | ================== 20 | 21 | .. toctree:: 22 | 6.0.0 23 | 6.1.0 24 | 25 | 5.x Release Series 26 | ================== 27 | 28 | .. toctree:: 29 | 5.0.4 30 | 5.0.3 31 | 5.0.2 32 | 5.0.1 33 | 5.0.0 34 | 35 | 4.x Release Series 36 | ================== 37 | 38 | .. toctree:: 39 | 4.0.1 40 | 4.0.0 41 | 42 | 3.x Release Series 43 | ================== 44 | 45 | .. toctree:: 46 | 3.9.2 47 | 3.9.1 48 | 3.9.0 49 | 3.8.4 50 | 3.8.3 51 | 3.8.2 52 | 3.8.1 53 | 3.8.0 54 | 3.7.9 55 | 3.7.8 56 | 3.7.7 57 | 3.7.6 58 | 3.7.5 59 | 3.7.4 60 | 3.7.3 61 | 3.7.2 62 | 3.7.1 63 | 3.7.0 64 | 3.6.0 65 | 3.5.0 66 | 3.4.1 67 | 3.4.0 68 | 3.3.0 69 | 3.2.1 70 | 3.2.0 71 | 3.1.1 72 | 3.1.0 73 | 3.0.4 74 | 3.0.3 75 | 3.0.2 76 | 3.0.1 77 | 3.0.0 78 | 79 | 2.x Release Series 80 | ================== 81 | 82 | .. toctree:: 83 | 2.6.2 84 | 2.6.1 85 | 2.6.0 86 | 2.5.5 87 | 2.5.4 88 | 2.5.3 89 | 2.5.2 90 | 2.5.1 91 | 2.5.0 92 | 2.4.1 93 | 2.4.0 94 | 2.3.0 95 | 2.2.5 96 | 2.2.4 97 | 2.2.3 98 | 2.2.2 99 | 2.2.1 100 | 2.2.0 101 | 2.1.0 102 | 2.0.0 103 | 104 | 1.x Release Series 105 | ================== 106 | 107 | .. toctree:: 108 | 1.7.0 109 | 1.6.2 110 | 1.6.1 111 | 1.6.0 112 | 1.5.0 113 | 1.4.0 114 | 1.3.1 115 | 1.3.0 116 | 1.2.0 117 | 1.1.0 118 | 1.0.0 119 | 120 | 0.x Release Series 121 | ================== 122 | 123 | .. toctree:: 124 | 0.9.0 125 | 0.8.0 126 | 0.7.0 127 | 0.6.0 128 | -------------------------------------------------------------------------------- /docs/source/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=2.1.0,!=3.1.0 2 | sphinx-rtd-theme>=1.2.2 3 | sphinx-prompt>=1.8.0 4 | docutils!=0.18 5 | -------------------------------------------------------------------------------- /docs/source/user/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/docs/source/user/.keep -------------------------------------------------------------------------------- /docs/source/user/index.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Using Flake8 3 | ============== 4 | 5 | |Flake8| can be used in many ways. A few: 6 | 7 | - invoked on the command-line 8 | 9 | - invoked via Python 10 | 11 | This guide will cover all of these and the nuances for using |Flake8|. 12 | 13 | .. note:: 14 | 15 | This portion of |Flake8|'s documentation does not cover installation. See 16 | the :ref:`installation-guide` section for how to install |Flake8|. 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | 21 | invocation 22 | configuration 23 | options 24 | error-codes 25 | violations 26 | using-plugins 27 | using-hooks 28 | python-api 29 | 30 | .. config files 31 | .. command-line tutorial 32 | .. VCS usage 33 | .. installing and using plugins 34 | -------------------------------------------------------------------------------- /docs/source/user/invocation.rst: -------------------------------------------------------------------------------- 1 | .. _invocation: 2 | 3 | ================= 4 | Invoking Flake8 5 | ================= 6 | 7 | Once you have :ref:`installed ` |Flake8|, you can begin 8 | using it. Most of the time, you will be able to generically invoke |Flake8| 9 | like so: 10 | 11 | .. prompt:: bash 12 | 13 | flake8 ... 14 | 15 | Where you simply allow the shell running in your terminal to locate |Flake8|. 16 | In some cases, though, you may have installed |Flake8| for multiple versions 17 | of Python (e.g., Python 3.13 and Python 3.14) and you need to call a specific 18 | version. In that case, you will have much better results using: 19 | 20 | .. prompt:: bash 21 | 22 | python3.13 -m flake8 23 | 24 | Or 25 | 26 | .. prompt:: bash 27 | 28 | python3.14 -m flake8 29 | 30 | Since that will tell the correct version of Python to run |Flake8|. 31 | 32 | .. note:: 33 | 34 | Installing |Flake8| once will not install it on both Python 3.13 and 35 | Python 3.14. It will only install it for the version of Python that 36 | is running pip. 37 | 38 | It is also possible to specify command-line options directly to |Flake8|: 39 | 40 | .. prompt:: bash 41 | 42 | flake8 --select E123 43 | 44 | Or 45 | 46 | .. prompt:: bash 47 | 48 | python -m flake8 --select E123 49 | 50 | .. note:: 51 | 52 | This is the last time we will show both versions of an invocation. 53 | From now on, we'll simply use ``flake8`` and assume that the user 54 | knows they can instead use ``python -m flake8``. 55 | 56 | It's also possible to narrow what |Flake8| will try to check by specifying 57 | exactly the paths and directories you want it to check. Let's assume that 58 | we have a directory with python files and sub-directories which have python 59 | files (and may have more sub-directories) called ``my_project``. Then if 60 | we only want errors from files found inside ``my_project`` we can do: 61 | 62 | .. prompt:: bash 63 | 64 | flake8 my_project 65 | 66 | And if we only want certain errors (e.g., ``E123``) from files in that 67 | directory we can also do: 68 | 69 | .. prompt:: bash 70 | 71 | flake8 --select E123 my_project 72 | 73 | If you want to explore more options that can be passed on the command-line, 74 | you can use the ``--help`` option: 75 | 76 | .. prompt:: bash 77 | 78 | flake8 --help 79 | 80 | And you should see something like: 81 | 82 | .. code:: 83 | 84 | Usage: flake8 [options] file file ... 85 | 86 | Options: 87 | --version show program's version number and exit 88 | -h, --help show this help message and exit 89 | 90 | ... 91 | -------------------------------------------------------------------------------- /docs/source/user/python-api.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | Public Python API 3 | =================== 4 | 5 | |Flake8| 3.0.0 presently does not have a public, stable Python API. 6 | 7 | When it does it will be located in :mod:`flake8.api` and that will 8 | be documented here. 9 | 10 | 11 | Legacy API 12 | ========== 13 | 14 | When |Flake8| broke its hard dependency on the tricky internals of 15 | pycodestyle, it lost the easy backwards compatibility as well. To help 16 | existing users of that API we have :mod:`flake8.api.legacy`. This module 17 | includes a couple classes (which are documented below) and a function. 18 | 19 | The main usage that the developers of Flake8 observed was using the 20 | :func:`~flake8.api.legacy.get_style_guide` function and then calling 21 | :meth:`~flake8.api.legacy.StyleGuide.check_files`. To a lesser extent, 22 | people also seemed to use the :meth:`~flake8.api.legacy.Report.get_statistics` 23 | method on what ``check_files`` returns. We then sought to preserve that 24 | API in this module. 25 | 26 | Let's look at an example piece of code together: 27 | 28 | .. code-block:: python 29 | 30 | from flake8.api import legacy as flake8 31 | 32 | 33 | style_guide = flake8.get_style_guide(ignore=['E24', 'W503']) 34 | report = style_guide.check_files([...]) 35 | assert report.get_statistics('E') == [], 'Flake8 found violations' 36 | 37 | This represents the basic universal usage of all existing Flake8 2.x 38 | integrations. Each example we found was obviously slightly different, 39 | but this is kind of the gist, so let's walk through this. 40 | 41 | Everything that is backwards compatible for our API is in the 42 | :mod:`flake8.api.legacy` submodule. This is to indicate, clearly, that 43 | the old API is being used. 44 | 45 | We create a |StyleGuide| by calling |style_guide|. We can pass options 46 | to |style_guide| that correspond to the command-line options one might use. 47 | For example, we can pass ``ignore``, ``select``, ``exclude``, ``format``, etc. 48 | Our legacy API, does not enforce legacy behaviour, so we can combine 49 | ``ignore`` and ``select`` like we might on the command-line, e.g., 50 | 51 | .. code-block:: python 52 | 53 | style_guide = flake8.get_style_guide( 54 | ignore=['E24', 'W5'], 55 | select=['E', 'W', 'F'], 56 | format='pylint', 57 | ) 58 | 59 | Once we have our |StyleGuide| we can use the same methods that we used before, 60 | namely 61 | 62 | .. automethod:: flake8.api.legacy.StyleGuide.check_files 63 | 64 | .. automethod:: flake8.api.legacy.StyleGuide.excluded 65 | 66 | .. automethod:: flake8.api.legacy.StyleGuide.init_report 67 | 68 | .. automethod:: flake8.api.legacy.StyleGuide.input_file 69 | 70 | .. warning:: 71 | 72 | These are not *perfectly* backwards compatible. Not all arguments are 73 | respected, and some of the types necessary for something to work have 74 | changed. 75 | 76 | Most people, we observed, were using 77 | :meth:`~flake8.api.legacy.StyleGuide.check_files`. You can use this to specify 78 | a list of filenames or directories to check. In |Flake8| 3.0, however, we 79 | return a different object that has similar methods. We return a |Report| which 80 | has the method 81 | 82 | .. automethod:: flake8.api.legacy.Report.get_statistics 83 | 84 | Most usage of this method that we noted was as documented above. Keep in mind, 85 | however, that it provides a list of strings and not anything more malleable. 86 | 87 | 88 | Autogenerated Legacy Documentation 89 | ---------------------------------- 90 | 91 | .. automodule:: flake8.api.legacy 92 | :members: 93 | 94 | .. autoclass:: flake8.api.legacy.StyleGuide 95 | :members: options, paths 96 | 97 | .. autoclass:: flake8.api.legacy.Report 98 | :members: total_errors 99 | 100 | 101 | .. |style_guide| replace:: :func:`flake8.api.legacy.get_style_guide` 102 | .. |StyleGuide| replace:: :class:`flake8.api.legacy.StyleGuide` 103 | .. |Report| replace:: :class:`flake8.api.legacy.Report` 104 | -------------------------------------------------------------------------------- /docs/source/user/using-hooks.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Using Version Control Hooks 3 | ============================= 4 | 5 | Usage with the `pre-commit`_ git hooks framework 6 | ================================================ 7 | 8 | |Flake8| can be included as a hook for `pre-commit`_. The easiest way to get 9 | started is to add this configuration to your ``.pre-commit-config.yaml``: 10 | 11 | .. code-block:: yaml 12 | 13 | - repo: https://github.com/pycqa/flake8 14 | rev: '' # pick a git hash / tag to point to 15 | hooks: 16 | - id: flake8 17 | 18 | See the `pre-commit docs`_ for how to customize this configuration. 19 | 20 | Checked-in python files will be passed as positional arguments. ``flake8`` 21 | will always lint explicitly passed arguments (:option:`flake8 --exclude` has 22 | no effect). Instead use ``pre-commit``'s ``exclude: ...`` regex to exclude 23 | files. ``pre-commit`` won't ever pass untracked files to ``flake8`` so 24 | excluding ``.git`` / ``.tox`` / etc. is unnecessary. 25 | 26 | .. code-block:: yaml 27 | 28 | - id: flake8 29 | exclude: ^testing/(data|examples)/ 30 | 31 | ``pre-commit`` creates an isolated environment for hooks. To use ``flake8`` 32 | plugins, use the ``additional_dependencies`` setting. 33 | 34 | .. code-block:: yaml 35 | 36 | - id: flake8 37 | additional_dependencies: [flake8-docstrings] 38 | 39 | .. _pre-commit: 40 | https://pre-commit.com/ 41 | .. _pre-commit docs: 42 | https://pre-commit.com/#pre-commit-configyaml---hooks 43 | -------------------------------------------------------------------------------- /docs/source/user/using-plugins.rst: -------------------------------------------------------------------------------- 1 | ================================== 2 | Using Plugins For Fun and Profit 3 | ================================== 4 | 5 | |Flake8| is useful on its own but a lot of |Flake8|'s popularity is due to 6 | its extensibility. Our community has developed :term:`plugin`\ s that augment 7 | |Flake8|'s behaviour. Most of these plugins are uploaded to PyPI_. The 8 | developers of these plugins often have some style they wish to enforce. 9 | 10 | For example, `flake8-docstrings`_ adds a check for :pep:`257` style 11 | conformance. Others attempt to enforce consistency, like `flake8-quotes`_. 12 | 13 | .. note:: 14 | 15 | The accuracy or reliability of these plugins may vary wildly from plugin 16 | to plugin and not all plugins are guaranteed to work with |Flake8| 3.0. 17 | 18 | To install a third-party plugin, make sure that you know which version of 19 | Python (or pip) you used to install |Flake8|. You can then use the most 20 | appropriate of: 21 | 22 | .. prompt:: bash 23 | 24 | pip install 25 | pip3 install 26 | python -m pip install 27 | python3 -m pip install 28 | python3.9 -m pip install 29 | 30 | To install the plugin, where ```` is the package name on PyPI_. 31 | To verify installation use: 32 | 33 | .. prompt:: bash 34 | 35 | flake8 --version 36 | python -m flake8 --version 37 | 38 | To see the plugin's name and version in the output. 39 | 40 | .. seealso:: :ref:`How to Invoke Flake8 ` 41 | 42 | After installation, most plugins immediately start reporting :term:`error`\ s. 43 | Check the plugin's documentation for which error codes it returns and if it 44 | disables any by default. 45 | 46 | .. note:: 47 | 48 | You can use both :option:`flake8 --select` and :option:`flake8 --ignore` 49 | with plugins. 50 | 51 | Some plugins register new options, so be sure to check :option:`flake8 --help` 52 | for new flags and documentation. These plugins may also allow these flags to 53 | be specified in your configuration file. Hopefully, the plugin authors have 54 | documented this for you. 55 | 56 | .. seealso:: :ref:`Configuring Flake8 ` 57 | 58 | 59 | .. _PyPI: 60 | https://pypi.org/ 61 | .. _flake8-docstrings: 62 | https://pypi.org/project/flake8-docstrings/ 63 | .. _flake8-quotes: 64 | https://pypi.org/project/flake8-quotes/ 65 | -------------------------------------------------------------------------------- /example-plugin/setup.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import setuptools 4 | 5 | setuptools.setup( 6 | name="flake8-example-plugin", 7 | license="MIT", 8 | version="1.0.0", 9 | description="Example plugin to Flake8", 10 | author="Ian Cordasco", 11 | author_email="graffatcolmingov@gmail.com", 12 | url="https://github.com/pycqa/flake8", 13 | package_dir={"": "src/"}, 14 | packages=["flake8_example_plugin"], 15 | entry_points={ 16 | "flake8.extension": [ 17 | "X1 = flake8_example_plugin:ExampleOne", 18 | "X2 = flake8_example_plugin:ExampleTwo", 19 | ], 20 | }, 21 | classifiers=[ 22 | "Framework :: Flake8", 23 | "License :: OSI Approved :: MIT License", 24 | "Programming Language :: Python", 25 | "Programming Language :: Python :: 3", 26 | "Topic :: Software Development :: Libraries :: Python Modules", 27 | "Topic :: Software Development :: Quality Assurance", 28 | ], 29 | ) 30 | -------------------------------------------------------------------------------- /example-plugin/src/flake8_example_plugin/__init__.py: -------------------------------------------------------------------------------- 1 | """Module for an example Flake8 plugin.""" 2 | from __future__ import annotations 3 | 4 | from .off_by_default import ExampleTwo 5 | from .on_by_default import ExampleOne 6 | 7 | __all__ = ( 8 | "ExampleOne", 9 | "ExampleTwo", 10 | ) 11 | -------------------------------------------------------------------------------- /example-plugin/src/flake8_example_plugin/off_by_default.py: -------------------------------------------------------------------------------- 1 | """Our first example plugin.""" 2 | from __future__ import annotations 3 | 4 | 5 | class ExampleTwo: 6 | """Second Example Plugin.""" 7 | 8 | off_by_default = True 9 | 10 | def __init__(self, tree): 11 | self.tree = tree 12 | 13 | def run(self): 14 | """Do nothing.""" 15 | yield ( 16 | 1, 17 | 0, 18 | "X200 The off-by-default plugin was enabled", 19 | "OffByDefaultPlugin", 20 | ) 21 | -------------------------------------------------------------------------------- /example-plugin/src/flake8_example_plugin/on_by_default.py: -------------------------------------------------------------------------------- 1 | """Our first example plugin.""" 2 | from __future__ import annotations 3 | 4 | 5 | class ExampleOne: 6 | """First Example Plugin.""" 7 | 8 | def __init__(self, tree): 9 | self.tree = tree 10 | 11 | def run(self): 12 | """Do nothing.""" 13 | yield from [] 14 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | norecursedirs = .git .* *.egg* docs dist build 3 | addopts = -rw 4 | filterwarnings = error 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = flake8 3 | version = attr: flake8.__version__ 4 | description = the modular source code checker: pep8 pyflakes and co 5 | long_description = file: README.rst 6 | long_description_content_type = text/x-rst 7 | url = https://github.com/pycqa/flake8 8 | author = Tarek Ziade 9 | author_email = tarek@ziade.org 10 | maintainer = Ian Stapleton Cordasco 11 | maintainer_email = graffatcolmingov@gmail.com 12 | license = MIT 13 | license_files = LICENSE 14 | classifiers = 15 | Development Status :: 5 - Production/Stable 16 | Environment :: Console 17 | Framework :: Flake8 18 | Intended Audience :: Developers 19 | License :: OSI Approved :: MIT License 20 | Programming Language :: Python 21 | Programming Language :: Python :: 3 22 | Programming Language :: Python :: 3 :: Only 23 | Programming Language :: Python :: Implementation :: CPython 24 | Programming Language :: Python :: Implementation :: PyPy 25 | Topic :: Software Development :: Libraries :: Python Modules 26 | Topic :: Software Development :: Quality Assurance 27 | 28 | [options] 29 | packages = find: 30 | install_requires = 31 | mccabe>=0.7.0,<0.8.0 32 | pycodestyle>=2.13.0,<2.14.0 33 | pyflakes>=3.3.0,<3.4.0 34 | python_requires = >=3.9 35 | package_dir = 36 | =src 37 | 38 | [options.packages.find] 39 | where = src 40 | 41 | [options.entry_points] 42 | console_scripts = 43 | flake8 = flake8.main.cli:main 44 | flake8.extension = 45 | F = flake8.plugins.pyflakes:FlakesChecker 46 | E = flake8.plugins.pycodestyle:pycodestyle_logical 47 | W = flake8.plugins.pycodestyle:pycodestyle_physical 48 | flake8.report = 49 | default = flake8.formatting.default:Default 50 | pylint = flake8.formatting.default:Pylint 51 | quiet-filename = flake8.formatting.default:FilenameOnly 52 | quiet-nothing = flake8.formatting.default:Nothing 53 | 54 | [bdist_wheel] 55 | universal = 1 56 | 57 | [coverage:run] 58 | source = 59 | flake8 60 | tests 61 | plugins = covdefaults 62 | 63 | [coverage:report] 64 | fail_under = 97 65 | 66 | [mypy] 67 | check_untyped_defs = true 68 | disallow_any_generics = true 69 | disallow_incomplete_defs = true 70 | disallow_untyped_defs = true 71 | no_implicit_optional = true 72 | warn_unused_ignores = true 73 | 74 | [mypy-tests.*] 75 | disallow_untyped_defs = false 76 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """Packaging logic for Flake8.""" 2 | from __future__ import annotations 3 | 4 | import os 5 | import sys 6 | 7 | import setuptools 8 | 9 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) 10 | 11 | setuptools.setup() 12 | -------------------------------------------------------------------------------- /src/flake8/__init__.py: -------------------------------------------------------------------------------- 1 | """Top-level module for Flake8. 2 | 3 | This module 4 | 5 | - initializes logging for the command-line tool 6 | - tracks the version of the package 7 | - provides a way to configure logging for the command-line tool 8 | 9 | .. autofunction:: flake8.configure_logging 10 | 11 | """ 12 | from __future__ import annotations 13 | 14 | import logging 15 | import sys 16 | 17 | LOG = logging.getLogger(__name__) 18 | LOG.addHandler(logging.NullHandler()) 19 | 20 | __version__ = "7.2.0" 21 | __version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit()) 22 | 23 | _VERBOSITY_TO_LOG_LEVEL = { 24 | # output more than warnings but not debugging info 25 | 1: logging.INFO, # INFO is a numerical level of 20 26 | # output debugging information 27 | 2: logging.DEBUG, # DEBUG is a numerical level of 10 28 | } 29 | 30 | LOG_FORMAT = ( 31 | "%(name)-25s %(processName)-11s %(relativeCreated)6d " 32 | "%(levelname)-8s %(message)s" 33 | ) 34 | 35 | 36 | def configure_logging( 37 | verbosity: int, 38 | filename: str | None = None, 39 | logformat: str = LOG_FORMAT, 40 | ) -> None: 41 | """Configure logging for flake8. 42 | 43 | :param verbosity: 44 | How verbose to be in logging information. 45 | :param filename: 46 | Name of the file to append log information to. 47 | If ``None`` this will log to ``sys.stderr``. 48 | If the name is "stdout" or "stderr" this will log to the appropriate 49 | stream. 50 | """ 51 | if verbosity <= 0: 52 | return 53 | 54 | verbosity = min(verbosity, max(_VERBOSITY_TO_LOG_LEVEL)) 55 | log_level = _VERBOSITY_TO_LOG_LEVEL[verbosity] 56 | 57 | if not filename or filename in ("stderr", "stdout"): 58 | fileobj = getattr(sys, filename or "stderr") 59 | handler_cls: type[logging.Handler] = logging.StreamHandler 60 | else: 61 | fileobj = filename 62 | handler_cls = logging.FileHandler 63 | 64 | handler = handler_cls(fileobj) 65 | handler.setFormatter(logging.Formatter(logformat)) 66 | LOG.addHandler(handler) 67 | LOG.setLevel(log_level) 68 | LOG.debug( 69 | "Added a %s logging handler to logger root at %s", filename, __name__ 70 | ) 71 | -------------------------------------------------------------------------------- /src/flake8/__main__.py: -------------------------------------------------------------------------------- 1 | """Module allowing for ``python -m flake8 ...``.""" 2 | from __future__ import annotations 3 | 4 | from flake8.main.cli import main 5 | 6 | if __name__ == "__main__": 7 | raise SystemExit(main()) 8 | -------------------------------------------------------------------------------- /src/flake8/_compat.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | import tokenize 5 | 6 | if sys.version_info >= (3, 12): # pragma: >=3.12 cover 7 | FSTRING_START = tokenize.FSTRING_START 8 | FSTRING_MIDDLE = tokenize.FSTRING_MIDDLE 9 | FSTRING_END = tokenize.FSTRING_END 10 | else: # pragma: <3.12 cover 11 | FSTRING_START = FSTRING_MIDDLE = FSTRING_END = -1 12 | 13 | if sys.version_info >= (3, 14): # pragma: >=3.14 cover 14 | TSTRING_START = tokenize.TSTRING_START 15 | TSTRING_MIDDLE = tokenize.TSTRING_MIDDLE 16 | TSTRING_END = tokenize.TSTRING_END 17 | else: # pragma: <3.14 cover 18 | TSTRING_START = TSTRING_MIDDLE = TSTRING_END = -1 19 | -------------------------------------------------------------------------------- /src/flake8/api/__init__.py: -------------------------------------------------------------------------------- 1 | """Module containing all public entry-points for Flake8. 2 | 3 | This is the only submodule in Flake8 with a guaranteed stable API. All other 4 | submodules are considered internal only and are subject to change. 5 | """ 6 | from __future__ import annotations 7 | -------------------------------------------------------------------------------- /src/flake8/defaults.py: -------------------------------------------------------------------------------- 1 | """Constants that define defaults.""" 2 | from __future__ import annotations 3 | 4 | import re 5 | 6 | EXCLUDE = ( 7 | ".svn", 8 | "CVS", 9 | ".bzr", 10 | ".hg", 11 | ".git", 12 | "__pycache__", 13 | ".tox", 14 | ".nox", 15 | ".eggs", 16 | "*.egg", 17 | ) 18 | IGNORE = ("E121", "E123", "E126", "E226", "E24", "E704", "W503", "W504") 19 | MAX_LINE_LENGTH = 79 20 | INDENT_SIZE = 4 21 | 22 | # Other constants 23 | WHITESPACE = frozenset(" \t") 24 | 25 | STATISTIC_NAMES = ("logical lines", "physical lines", "tokens") 26 | 27 | NOQA_INLINE_REGEXP = re.compile( 28 | # We're looking for items that look like this: 29 | # ``# noqa`` 30 | # ``# noqa: E123`` 31 | # ``# noqa: E123,W451,F921`` 32 | # ``# noqa:E123,W451,F921`` 33 | # ``# NoQA: E123,W451,F921`` 34 | # ``# NOQA: E123,W451,F921`` 35 | # ``# NOQA:E123,W451,F921`` 36 | # We do not want to capture the ``: `` that follows ``noqa`` 37 | # We do not care about the casing of ``noqa`` 38 | # We want a comma-separated list of errors 39 | r"# noqa(?::[\s]?(?P([A-Z]+[0-9]+(?:[,\s]+)?)+))?", 40 | re.IGNORECASE, 41 | ) 42 | 43 | NOQA_FILE = re.compile(r"\s*# flake8[:=]\s*noqa", re.I) 44 | 45 | VALID_CODE_PREFIX = re.compile("^[A-Z]{1,3}[0-9]{0,3}$", re.ASCII) 46 | -------------------------------------------------------------------------------- /src/flake8/discover_files.py: -------------------------------------------------------------------------------- 1 | """Functions related to discovering paths.""" 2 | from __future__ import annotations 3 | 4 | import logging 5 | import os.path 6 | from collections.abc import Generator 7 | from collections.abc import Sequence 8 | from typing import Callable 9 | 10 | from flake8 import utils 11 | 12 | LOG = logging.getLogger(__name__) 13 | 14 | 15 | def _filenames_from( 16 | arg: str, 17 | *, 18 | predicate: Callable[[str], bool], 19 | ) -> Generator[str]: 20 | """Generate filenames from an argument. 21 | 22 | :param arg: 23 | Parameter from the command-line. 24 | :param predicate: 25 | Predicate to use to filter out filenames. If the predicate 26 | returns ``True`` we will exclude the filename, otherwise we 27 | will yield it. By default, we include every filename 28 | generated. 29 | :returns: 30 | Generator of paths 31 | """ 32 | if predicate(arg): 33 | return 34 | 35 | if os.path.isdir(arg): 36 | for root, sub_directories, files in os.walk(arg): 37 | # NOTE(sigmavirus24): os.walk() will skip a directory if you 38 | # remove it from the list of sub-directories. 39 | for directory in tuple(sub_directories): 40 | joined = os.path.join(root, directory) 41 | if predicate(joined): 42 | sub_directories.remove(directory) 43 | 44 | for filename in files: 45 | joined = os.path.join(root, filename) 46 | if not predicate(joined): 47 | yield joined 48 | else: 49 | yield arg 50 | 51 | 52 | def expand_paths( 53 | *, 54 | paths: Sequence[str], 55 | stdin_display_name: str, 56 | filename_patterns: Sequence[str], 57 | exclude: Sequence[str], 58 | ) -> Generator[str]: 59 | """Expand out ``paths`` from commandline to the lintable files.""" 60 | if not paths: 61 | paths = ["."] 62 | 63 | def is_excluded(arg: str) -> bool: 64 | if arg == "-": 65 | # if the stdin_display_name is the default, always include it 66 | if stdin_display_name == "stdin": 67 | return False 68 | arg = stdin_display_name 69 | 70 | return utils.matches_filename( 71 | arg, 72 | patterns=exclude, 73 | log_message='"%(path)s" has %(whether)sbeen excluded', 74 | logger=LOG, 75 | ) 76 | 77 | return ( 78 | filename 79 | for path in paths 80 | for filename in _filenames_from(path, predicate=is_excluded) 81 | if ( 82 | # always lint `-` 83 | filename == "-" 84 | # always lint explicitly passed (even if not matching filter) 85 | or path == filename 86 | # otherwise, check the file against filtered patterns 87 | or utils.fnmatch(filename, filename_patterns) 88 | ) 89 | ) 90 | -------------------------------------------------------------------------------- /src/flake8/exceptions.py: -------------------------------------------------------------------------------- 1 | """Exception classes for all of Flake8.""" 2 | from __future__ import annotations 3 | 4 | 5 | class Flake8Exception(Exception): 6 | """Plain Flake8 exception.""" 7 | 8 | 9 | class EarlyQuit(Flake8Exception): 10 | """Except raised when encountering a KeyboardInterrupt.""" 11 | 12 | 13 | class ExecutionError(Flake8Exception): 14 | """Exception raised during execution of Flake8.""" 15 | 16 | 17 | class FailedToLoadPlugin(Flake8Exception): 18 | """Exception raised when a plugin fails to load.""" 19 | 20 | FORMAT = 'Flake8 failed to load plugin "%(name)s" due to %(exc)s.' 21 | 22 | def __init__(self, plugin_name: str, exception: Exception) -> None: 23 | """Initialize our FailedToLoadPlugin exception.""" 24 | self.plugin_name = plugin_name 25 | self.original_exception = exception 26 | super().__init__(plugin_name, exception) 27 | 28 | def __str__(self) -> str: 29 | """Format our exception message.""" 30 | return self.FORMAT % { 31 | "name": self.plugin_name, 32 | "exc": self.original_exception, 33 | } 34 | 35 | 36 | class PluginRequestedUnknownParameters(Flake8Exception): 37 | """The plugin requested unknown parameters.""" 38 | 39 | FORMAT = '"%(name)s" requested unknown parameters causing %(exc)s' 40 | 41 | def __init__(self, plugin_name: str, exception: Exception) -> None: 42 | """Pop certain keyword arguments for initialization.""" 43 | self.plugin_name = plugin_name 44 | self.original_exception = exception 45 | super().__init__(plugin_name, exception) 46 | 47 | def __str__(self) -> str: 48 | """Format our exception message.""" 49 | return self.FORMAT % { 50 | "name": self.plugin_name, 51 | "exc": self.original_exception, 52 | } 53 | 54 | 55 | class PluginExecutionFailed(Flake8Exception): 56 | """The plugin failed during execution.""" 57 | 58 | FORMAT = '{fname}: "{plugin}" failed during execution due to {exc!r}' 59 | 60 | def __init__( 61 | self, 62 | filename: str, 63 | plugin_name: str, 64 | exception: Exception, 65 | ) -> None: 66 | """Utilize keyword arguments for message generation.""" 67 | self.filename = filename 68 | self.plugin_name = plugin_name 69 | self.original_exception = exception 70 | super().__init__(filename, plugin_name, exception) 71 | 72 | def __str__(self) -> str: 73 | """Format our exception message.""" 74 | return self.FORMAT.format( 75 | fname=self.filename, 76 | plugin=self.plugin_name, 77 | exc=self.original_exception, 78 | ) 79 | -------------------------------------------------------------------------------- /src/flake8/formatting/__init__.py: -------------------------------------------------------------------------------- 1 | """Submodule containing the default formatters for Flake8.""" 2 | from __future__ import annotations 3 | -------------------------------------------------------------------------------- /src/flake8/formatting/_windows_color.py: -------------------------------------------------------------------------------- 1 | """ctypes hackery to enable color processing on windows. 2 | 3 | See: https://github.com/pre-commit/pre-commit/blob/cb40e96/pre_commit/color.py 4 | """ 5 | from __future__ import annotations 6 | 7 | import sys 8 | 9 | if sys.platform == "win32": # pragma: no cover (windows) 10 | 11 | def _enable() -> None: 12 | from ctypes import POINTER 13 | from ctypes import windll 14 | from ctypes import WinError 15 | from ctypes import WINFUNCTYPE 16 | from ctypes.wintypes import BOOL 17 | from ctypes.wintypes import DWORD 18 | from ctypes.wintypes import HANDLE 19 | 20 | STD_ERROR_HANDLE = -12 21 | ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 22 | 23 | def bool_errcheck(result, func, args): 24 | if not result: 25 | raise WinError() 26 | return args 27 | 28 | GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)( 29 | ("GetStdHandle", windll.kernel32), 30 | ((1, "nStdHandle"),), 31 | ) 32 | 33 | GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))( 34 | ("GetConsoleMode", windll.kernel32), 35 | ((1, "hConsoleHandle"), (2, "lpMode")), 36 | ) 37 | GetConsoleMode.errcheck = bool_errcheck 38 | 39 | SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)( 40 | ("SetConsoleMode", windll.kernel32), 41 | ((1, "hConsoleHandle"), (1, "dwMode")), 42 | ) 43 | SetConsoleMode.errcheck = bool_errcheck 44 | 45 | # As of Windows 10, the Windows console supports (some) ANSI escape 46 | # sequences, but it needs to be enabled using `SetConsoleMode` first. 47 | # 48 | # More info on the escape sequences supported: 49 | # https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx 50 | stderr = GetStdHandle(STD_ERROR_HANDLE) 51 | flags = GetConsoleMode(stderr) 52 | SetConsoleMode(stderr, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING) 53 | 54 | try: 55 | _enable() 56 | except OSError: 57 | terminal_supports_color = False 58 | else: 59 | terminal_supports_color = True 60 | else: # pragma: win32 no cover 61 | terminal_supports_color = True 62 | -------------------------------------------------------------------------------- /src/flake8/formatting/default.py: -------------------------------------------------------------------------------- 1 | """Default formatting class for Flake8.""" 2 | from __future__ import annotations 3 | 4 | from flake8.formatting import base 5 | from flake8.violation import Violation 6 | 7 | COLORS = { 8 | "bold": "\033[1m", 9 | "black": "\033[30m", 10 | "red": "\033[31m", 11 | "green": "\033[32m", 12 | "yellow": "\033[33m", 13 | "blue": "\033[34m", 14 | "magenta": "\033[35m", 15 | "cyan": "\033[36m", 16 | "white": "\033[37m", 17 | "reset": "\033[m", 18 | } 19 | COLORS_OFF = {k: "" for k in COLORS} 20 | 21 | 22 | class SimpleFormatter(base.BaseFormatter): 23 | """Simple abstraction for Default and Pylint formatter commonality. 24 | 25 | Sub-classes of this need to define an ``error_format`` attribute in order 26 | to succeed. The ``format`` method relies on that attribute and expects the 27 | ``error_format`` string to use the old-style formatting strings with named 28 | parameters: 29 | 30 | * code 31 | * text 32 | * path 33 | * row 34 | * col 35 | 36 | """ 37 | 38 | error_format: str 39 | 40 | def format(self, error: Violation) -> str | None: 41 | """Format and write error out. 42 | 43 | If an output filename is specified, write formatted errors to that 44 | file. Otherwise, print the formatted error to standard out. 45 | """ 46 | return self.error_format % { 47 | "code": error.code, 48 | "text": error.text, 49 | "path": error.filename, 50 | "row": error.line_number, 51 | "col": error.column_number, 52 | **(COLORS if self.color else COLORS_OFF), 53 | } 54 | 55 | 56 | class Default(SimpleFormatter): 57 | """Default formatter for Flake8. 58 | 59 | This also handles backwards compatibility for people specifying a custom 60 | format string. 61 | """ 62 | 63 | error_format = ( 64 | "%(bold)s%(path)s%(reset)s" 65 | "%(cyan)s:%(reset)s%(row)d%(cyan)s:%(reset)s%(col)d%(cyan)s:%(reset)s " 66 | "%(bold)s%(red)s%(code)s%(reset)s %(text)s" 67 | ) 68 | 69 | def after_init(self) -> None: 70 | """Check for a custom format string.""" 71 | if self.options.format.lower() != "default": 72 | self.error_format = self.options.format 73 | 74 | 75 | class Pylint(SimpleFormatter): 76 | """Pylint formatter for Flake8.""" 77 | 78 | error_format = "%(path)s:%(row)d: [%(code)s] %(text)s" 79 | 80 | 81 | class FilenameOnly(SimpleFormatter): 82 | """Only print filenames, e.g., flake8 -q.""" 83 | 84 | error_format = "%(path)s" 85 | 86 | def after_init(self) -> None: 87 | """Initialize our set of filenames.""" 88 | self.filenames_already_printed: set[str] = set() 89 | 90 | def show_source(self, error: Violation) -> str | None: 91 | """Do not include the source code.""" 92 | 93 | def format(self, error: Violation) -> str | None: 94 | """Ensure we only print each error once.""" 95 | if error.filename not in self.filenames_already_printed: 96 | self.filenames_already_printed.add(error.filename) 97 | return super().format(error) 98 | else: 99 | return None 100 | 101 | 102 | class Nothing(base.BaseFormatter): 103 | """Print absolutely nothing.""" 104 | 105 | def format(self, error: Violation) -> str | None: 106 | """Do nothing.""" 107 | 108 | def show_source(self, error: Violation) -> str | None: 109 | """Do not print the source.""" 110 | -------------------------------------------------------------------------------- /src/flake8/main/__init__.py: -------------------------------------------------------------------------------- 1 | """Module containing the logic for the Flake8 entry-points.""" 2 | from __future__ import annotations 3 | -------------------------------------------------------------------------------- /src/flake8/main/cli.py: -------------------------------------------------------------------------------- 1 | """Command-line implementation of flake8.""" 2 | from __future__ import annotations 3 | 4 | import sys 5 | from collections.abc import Sequence 6 | 7 | from flake8.main import application 8 | 9 | 10 | def main(argv: Sequence[str] | None = None) -> int: 11 | """Execute the main bit of the application. 12 | 13 | This handles the creation of an instance of :class:`Application`, runs it, 14 | and then exits the application. 15 | 16 | :param argv: 17 | The arguments to be passed to the application for parsing. 18 | """ 19 | if argv is None: 20 | argv = sys.argv[1:] 21 | 22 | app = application.Application() 23 | app.run(argv) 24 | return app.exit_code() 25 | -------------------------------------------------------------------------------- /src/flake8/main/debug.py: -------------------------------------------------------------------------------- 1 | """Module containing the logic for our debugging logic.""" 2 | from __future__ import annotations 3 | 4 | import platform 5 | from typing import Any 6 | 7 | from flake8.plugins.finder import Plugins 8 | 9 | 10 | def information(version: str, plugins: Plugins) -> dict[str, Any]: 11 | """Generate the information to be printed for the bug report.""" 12 | versions = sorted( 13 | { 14 | (loaded.plugin.package, loaded.plugin.version) 15 | for loaded in plugins.all_plugins() 16 | if loaded.plugin.package not in {"flake8", "local"} 17 | } 18 | ) 19 | return { 20 | "version": version, 21 | "plugins": [ 22 | {"plugin": plugin, "version": version} 23 | for plugin, version in versions 24 | ], 25 | "platform": { 26 | "python_implementation": platform.python_implementation(), 27 | "python_version": platform.python_version(), 28 | "system": platform.system(), 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /src/flake8/options/__init__.py: -------------------------------------------------------------------------------- 1 | """Package containing the option manager and config management logic. 2 | 3 | - :mod:`flake8.options.config` contains the logic for finding, parsing, and 4 | merging configuration files. 5 | 6 | - :mod:`flake8.options.manager` contains the logic for managing customized 7 | Flake8 command-line and configuration options. 8 | 9 | - :mod:`flake8.options.aggregator` uses objects from both of the above modules 10 | to aggregate configuration into one object used by plugins and Flake8. 11 | 12 | """ 13 | from __future__ import annotations 14 | -------------------------------------------------------------------------------- /src/flake8/options/aggregator.py: -------------------------------------------------------------------------------- 1 | """Aggregation function for CLI specified options and config file options. 2 | 3 | This holds the logic that uses the collected and merged config files and 4 | applies the user-specified command-line configuration on top of it. 5 | """ 6 | from __future__ import annotations 7 | 8 | import argparse 9 | import configparser 10 | import logging 11 | from collections.abc import Sequence 12 | 13 | from flake8.options import config 14 | from flake8.options.manager import OptionManager 15 | 16 | LOG = logging.getLogger(__name__) 17 | 18 | 19 | def aggregate_options( 20 | manager: OptionManager, 21 | cfg: configparser.RawConfigParser, 22 | cfg_dir: str, 23 | argv: Sequence[str] | None, 24 | ) -> argparse.Namespace: 25 | """Aggregate and merge CLI and config file options.""" 26 | # Get defaults from the option parser 27 | default_values = manager.parse_args([]) 28 | 29 | # Get the parsed config 30 | parsed_config = config.parse_config(manager, cfg, cfg_dir) 31 | 32 | # store the plugin-set extended default ignore / select 33 | default_values.extended_default_ignore = manager.extended_default_ignore 34 | default_values.extended_default_select = manager.extended_default_select 35 | 36 | # Merge values parsed from config onto the default values returned 37 | for config_name, value in parsed_config.items(): 38 | dest_name = config_name 39 | # If the config name is somehow different from the destination name, 40 | # fetch the destination name from our Option 41 | if not hasattr(default_values, config_name): 42 | dest_val = manager.config_options_dict[config_name].dest 43 | assert isinstance(dest_val, str) 44 | dest_name = dest_val 45 | 46 | LOG.debug( 47 | 'Overriding default value of (%s) for "%s" with (%s)', 48 | getattr(default_values, dest_name, None), 49 | dest_name, 50 | value, 51 | ) 52 | # Override the default values with the config values 53 | setattr(default_values, dest_name, value) 54 | 55 | # Finally parse the command-line options 56 | return manager.parse_args(argv, default_values) 57 | -------------------------------------------------------------------------------- /src/flake8/options/config.py: -------------------------------------------------------------------------------- 1 | """Config handling logic for Flake8.""" 2 | from __future__ import annotations 3 | 4 | import configparser 5 | import logging 6 | import os.path 7 | from typing import Any 8 | 9 | from flake8 import exceptions 10 | from flake8.defaults import VALID_CODE_PREFIX 11 | from flake8.options.manager import OptionManager 12 | 13 | LOG = logging.getLogger(__name__) 14 | 15 | 16 | def _stat_key(s: str) -> tuple[int, int]: 17 | # same as what's used by samefile / samestat 18 | st = os.stat(s) 19 | return st.st_ino, st.st_dev 20 | 21 | 22 | def _find_config_file(path: str) -> str | None: 23 | # on windows if the homedir isn't detected this returns back `~` 24 | home = os.path.expanduser("~") 25 | try: 26 | home_stat = _stat_key(home) if home != "~" else None 27 | except OSError: # FileNotFoundError / PermissionError / etc. 28 | home_stat = None 29 | 30 | dir_stat = _stat_key(path) 31 | while True: 32 | for candidate in ("setup.cfg", "tox.ini", ".flake8"): 33 | cfg = configparser.RawConfigParser() 34 | cfg_path = os.path.join(path, candidate) 35 | try: 36 | cfg.read(cfg_path, encoding="UTF-8") 37 | except (UnicodeDecodeError, configparser.ParsingError) as e: 38 | LOG.warning("ignoring unparseable config %s: %s", cfg_path, e) 39 | else: 40 | # only consider it a config if it contains flake8 sections 41 | if "flake8" in cfg or "flake8:local-plugins" in cfg: 42 | return cfg_path 43 | 44 | new_path = os.path.dirname(path) 45 | new_dir_stat = _stat_key(new_path) 46 | if new_dir_stat == dir_stat or new_dir_stat == home_stat: 47 | break 48 | else: 49 | path = new_path 50 | dir_stat = new_dir_stat 51 | 52 | # did not find any configuration file 53 | return None 54 | 55 | 56 | def load_config( 57 | config: str | None, 58 | extra: list[str], 59 | *, 60 | isolated: bool = False, 61 | ) -> tuple[configparser.RawConfigParser, str]: 62 | """Load the configuration given the user options. 63 | 64 | - in ``isolated`` mode, return an empty configuration 65 | - if a config file is given in ``config`` use that, otherwise attempt to 66 | discover a configuration using ``tox.ini`` / ``setup.cfg`` / ``.flake8`` 67 | - finally, load any ``extra`` configuration files 68 | """ 69 | pwd = os.path.abspath(".") 70 | 71 | if isolated: 72 | return configparser.RawConfigParser(), pwd 73 | 74 | if config is None: 75 | config = _find_config_file(pwd) 76 | 77 | cfg = configparser.RawConfigParser() 78 | if config is not None: 79 | if not cfg.read(config, encoding="UTF-8"): 80 | raise exceptions.ExecutionError( 81 | f"The specified config file does not exist: {config}" 82 | ) 83 | cfg_dir = os.path.dirname(config) 84 | else: 85 | cfg_dir = pwd 86 | 87 | # TODO: remove this and replace it with configuration modifying plugins 88 | # read the additional configs afterwards 89 | for filename in extra: 90 | if not cfg.read(filename, encoding="UTF-8"): 91 | raise exceptions.ExecutionError( 92 | f"The specified config file does not exist: {filename}" 93 | ) 94 | 95 | return cfg, cfg_dir 96 | 97 | 98 | def parse_config( 99 | option_manager: OptionManager, 100 | cfg: configparser.RawConfigParser, 101 | cfg_dir: str, 102 | ) -> dict[str, Any]: 103 | """Parse and normalize the typed configuration options.""" 104 | if "flake8" not in cfg: 105 | return {} 106 | 107 | config_dict = {} 108 | 109 | for option_name in cfg["flake8"]: 110 | option = option_manager.config_options_dict.get(option_name) 111 | if option is None: 112 | LOG.debug('Option "%s" is not registered. Ignoring.', option_name) 113 | continue 114 | 115 | # Use the appropriate method to parse the config value 116 | value: Any 117 | if option.type is int or option.action == "count": 118 | value = cfg.getint("flake8", option_name) 119 | elif option.action in {"store_true", "store_false"}: 120 | value = cfg.getboolean("flake8", option_name) 121 | else: 122 | value = cfg.get("flake8", option_name) 123 | 124 | LOG.debug('Option "%s" returned value: %r', option_name, value) 125 | 126 | final_value = option.normalize(value, cfg_dir) 127 | 128 | if option_name in {"ignore", "extend-ignore"}: 129 | for error_code in final_value: 130 | if not VALID_CODE_PREFIX.match(error_code): 131 | raise ValueError( 132 | f"Error code {error_code!r} " 133 | f"supplied to {option_name!r} option " 134 | f"does not match {VALID_CODE_PREFIX.pattern!r}" 135 | ) 136 | 137 | assert option.config_name is not None 138 | config_dict[option.config_name] = final_value 139 | 140 | return config_dict 141 | -------------------------------------------------------------------------------- /src/flake8/options/parse_args.py: -------------------------------------------------------------------------------- 1 | """Procedure for parsing args, config, loading plugins.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | from collections.abc import Sequence 6 | 7 | import flake8 8 | from flake8.main import options 9 | from flake8.options import aggregator 10 | from flake8.options import config 11 | from flake8.options import manager 12 | from flake8.plugins import finder 13 | 14 | 15 | def parse_args( 16 | argv: Sequence[str], 17 | ) -> tuple[finder.Plugins, argparse.Namespace]: 18 | """Procedure for parsing args, config, loading plugins.""" 19 | prelim_parser = options.stage1_arg_parser() 20 | 21 | args0, rest = prelim_parser.parse_known_args(argv) 22 | # XXX (ericvw): Special case "forwarding" the output file option so 23 | # that it can be reparsed again for the BaseFormatter.filename. 24 | if args0.output_file: 25 | rest.extend(("--output-file", args0.output_file)) 26 | 27 | flake8.configure_logging(args0.verbose, args0.output_file) 28 | 29 | cfg, cfg_dir = config.load_config( 30 | config=args0.config, 31 | extra=args0.append_config, 32 | isolated=args0.isolated, 33 | ) 34 | 35 | plugin_opts = finder.parse_plugin_options( 36 | cfg, 37 | cfg_dir, 38 | enable_extensions=args0.enable_extensions, 39 | require_plugins=args0.require_plugins, 40 | ) 41 | raw_plugins = finder.find_plugins(cfg, plugin_opts) 42 | plugins = finder.load_plugins(raw_plugins, plugin_opts) 43 | 44 | option_manager = manager.OptionManager( 45 | version=flake8.__version__, 46 | plugin_versions=plugins.versions_str(), 47 | parents=[prelim_parser], 48 | formatter_names=list(plugins.reporters), 49 | ) 50 | options.register_default_options(option_manager) 51 | option_manager.register_plugins(plugins) 52 | 53 | opts = aggregator.aggregate_options(option_manager, cfg, cfg_dir, rest) 54 | 55 | for loaded in plugins.all_plugins(): 56 | parse_options = getattr(loaded.obj, "parse_options", None) 57 | if parse_options is None: 58 | continue 59 | 60 | # XXX: ideally we wouldn't have two forms of parse_options 61 | try: 62 | parse_options( 63 | option_manager, 64 | opts, 65 | opts.filenames, 66 | ) 67 | except TypeError: 68 | parse_options(opts) 69 | 70 | return plugins, opts 71 | -------------------------------------------------------------------------------- /src/flake8/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | """Submodule of built-in plugins and plugin managers.""" 2 | from __future__ import annotations 3 | -------------------------------------------------------------------------------- /src/flake8/plugins/pyflakes.py: -------------------------------------------------------------------------------- 1 | """Plugin built-in to Flake8 to treat pyflakes as a plugin.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | import ast 6 | import logging 7 | from collections.abc import Generator 8 | from typing import Any 9 | 10 | import pyflakes.checker 11 | 12 | from flake8.options.manager import OptionManager 13 | 14 | LOG = logging.getLogger(__name__) 15 | 16 | FLAKE8_PYFLAKES_CODES = { 17 | "UnusedImport": "F401", 18 | "ImportShadowedByLoopVar": "F402", 19 | "ImportStarUsed": "F403", 20 | "LateFutureImport": "F404", 21 | "ImportStarUsage": "F405", 22 | "ImportStarNotPermitted": "F406", 23 | "FutureFeatureNotDefined": "F407", 24 | "PercentFormatInvalidFormat": "F501", 25 | "PercentFormatExpectedMapping": "F502", 26 | "PercentFormatExpectedSequence": "F503", 27 | "PercentFormatExtraNamedArguments": "F504", 28 | "PercentFormatMissingArgument": "F505", 29 | "PercentFormatMixedPositionalAndNamed": "F506", 30 | "PercentFormatPositionalCountMismatch": "F507", 31 | "PercentFormatStarRequiresSequence": "F508", 32 | "PercentFormatUnsupportedFormatCharacter": "F509", 33 | "StringDotFormatInvalidFormat": "F521", 34 | "StringDotFormatExtraNamedArguments": "F522", 35 | "StringDotFormatExtraPositionalArguments": "F523", 36 | "StringDotFormatMissingArgument": "F524", 37 | "StringDotFormatMixingAutomatic": "F525", 38 | "FStringMissingPlaceholders": "F541", 39 | "MultiValueRepeatedKeyLiteral": "F601", 40 | "MultiValueRepeatedKeyVariable": "F602", 41 | "TooManyExpressionsInStarredAssignment": "F621", 42 | "TwoStarredExpressions": "F622", 43 | "AssertTuple": "F631", 44 | "IsLiteral": "F632", 45 | "InvalidPrintSyntax": "F633", 46 | "IfTuple": "F634", 47 | "BreakOutsideLoop": "F701", 48 | "ContinueOutsideLoop": "F702", 49 | "YieldOutsideFunction": "F704", 50 | "ReturnOutsideFunction": "F706", 51 | "DefaultExceptNotLast": "F707", 52 | "DoctestSyntaxError": "F721", 53 | "ForwardAnnotationSyntaxError": "F722", 54 | "RedefinedWhileUnused": "F811", 55 | "UndefinedName": "F821", 56 | "UndefinedExport": "F822", 57 | "UndefinedLocal": "F823", 58 | "UnusedIndirectAssignment": "F824", 59 | "DuplicateArgument": "F831", 60 | "UnusedVariable": "F841", 61 | "UnusedAnnotation": "F842", 62 | "RaiseNotImplemented": "F901", 63 | } 64 | 65 | 66 | class FlakesChecker(pyflakes.checker.Checker): 67 | """Subclass the Pyflakes checker to conform with the flake8 API.""" 68 | 69 | with_doctest = False 70 | 71 | def __init__(self, tree: ast.AST, filename: str) -> None: 72 | """Initialize the PyFlakes plugin with an AST tree and filename.""" 73 | super().__init__( 74 | tree, filename=filename, withDoctest=self.with_doctest 75 | ) 76 | 77 | @classmethod 78 | def add_options(cls, parser: OptionManager) -> None: 79 | """Register options for PyFlakes on the Flake8 OptionManager.""" 80 | parser.add_option( 81 | "--builtins", 82 | parse_from_config=True, 83 | comma_separated_list=True, 84 | help="define more built-ins, comma separated", 85 | ) 86 | parser.add_option( 87 | "--doctests", 88 | default=False, 89 | action="store_true", 90 | parse_from_config=True, 91 | help="also check syntax of the doctests", 92 | ) 93 | 94 | @classmethod 95 | def parse_options(cls, options: argparse.Namespace) -> None: 96 | """Parse option values from Flake8's OptionManager.""" 97 | if options.builtins: 98 | cls.builtIns = cls.builtIns.union(options.builtins) 99 | cls.with_doctest = options.doctests 100 | 101 | def run(self) -> Generator[tuple[int, int, str, type[Any]]]: 102 | """Run the plugin.""" 103 | for message in self.messages: 104 | col = getattr(message, "col", 0) 105 | yield ( 106 | message.lineno, 107 | col, 108 | "{} {}".format( 109 | FLAKE8_PYFLAKES_CODES.get(type(message).__name__, "F999"), 110 | message.message % message.message_args, 111 | ), 112 | message.__class__, 113 | ) 114 | -------------------------------------------------------------------------------- /src/flake8/plugins/reporter.py: -------------------------------------------------------------------------------- 1 | """Functions for constructing the requested report plugin.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | import logging 6 | 7 | from flake8.formatting.base import BaseFormatter 8 | from flake8.plugins.finder import LoadedPlugin 9 | 10 | LOG = logging.getLogger(__name__) 11 | 12 | 13 | def make( 14 | reporters: dict[str, LoadedPlugin], 15 | options: argparse.Namespace, 16 | ) -> BaseFormatter: 17 | """Make the formatter from the requested user options. 18 | 19 | - if :option:`flake8 --quiet` is specified, return the ``quiet-filename`` 20 | formatter. 21 | - if :option:`flake8 --quiet` is specified at least twice, return the 22 | ``quiet-nothing`` formatter. 23 | - otherwise attempt to return the formatter by name. 24 | - failing that, assume it is a format string and return the ``default`` 25 | formatter. 26 | """ 27 | format_name = options.format 28 | if options.quiet == 1: 29 | format_name = "quiet-filename" 30 | elif options.quiet >= 2: 31 | format_name = "quiet-nothing" 32 | 33 | try: 34 | format_plugin = reporters[format_name] 35 | except KeyError: 36 | LOG.warning( 37 | "%r is an unknown formatter. Falling back to default.", 38 | format_name, 39 | ) 40 | format_plugin = reporters["default"] 41 | 42 | return format_plugin.obj(options) 43 | -------------------------------------------------------------------------------- /src/flake8/statistics.py: -------------------------------------------------------------------------------- 1 | """Statistic collection logic for Flake8.""" 2 | from __future__ import annotations 3 | 4 | from collections.abc import Generator 5 | from typing import NamedTuple 6 | 7 | from flake8.violation import Violation 8 | 9 | 10 | class Statistics: 11 | """Manager of aggregated statistics for a run of Flake8.""" 12 | 13 | def __init__(self) -> None: 14 | """Initialize the underlying dictionary for our statistics.""" 15 | self._store: dict[Key, Statistic] = {} 16 | 17 | def error_codes(self) -> list[str]: 18 | """Return all unique error codes stored. 19 | 20 | :returns: 21 | Sorted list of error codes. 22 | """ 23 | return sorted({key.code for key in self._store}) 24 | 25 | def record(self, error: Violation) -> None: 26 | """Add the fact that the error was seen in the file. 27 | 28 | :param error: 29 | The Violation instance containing the information about the 30 | violation. 31 | """ 32 | key = Key.create_from(error) 33 | if key not in self._store: 34 | self._store[key] = Statistic.create_from(error) 35 | self._store[key].increment() 36 | 37 | def statistics_for( 38 | self, prefix: str, filename: str | None = None 39 | ) -> Generator[Statistic]: 40 | """Generate statistics for the prefix and filename. 41 | 42 | If you have a :class:`Statistics` object that has recorded errors, 43 | you can generate the statistics for a prefix (e.g., ``E``, ``E1``, 44 | ``W50``, ``W503``) with the optional filter of a filename as well. 45 | 46 | .. code-block:: python 47 | 48 | >>> stats = Statistics() 49 | >>> stats.statistics_for('E12', 50 | filename='src/flake8/statistics.py') 51 | 52 | >>> stats.statistics_for('W') 53 | 54 | 55 | :param prefix: 56 | The error class or specific error code to find statistics for. 57 | :param filename: 58 | (Optional) The filename to further filter results by. 59 | :returns: 60 | Generator of instances of :class:`Statistic` 61 | """ 62 | matching_errors = sorted( 63 | key for key in self._store if key.matches(prefix, filename) 64 | ) 65 | for error_code in matching_errors: 66 | yield self._store[error_code] 67 | 68 | 69 | class Key(NamedTuple): 70 | """Simple key structure for the Statistics dictionary. 71 | 72 | To make things clearer, easier to read, and more understandable, we use a 73 | namedtuple here for all Keys in the underlying dictionary for the 74 | Statistics object. 75 | """ 76 | 77 | filename: str 78 | code: str 79 | 80 | @classmethod 81 | def create_from(cls, error: Violation) -> Key: 82 | """Create a Key from :class:`flake8.violation.Violation`.""" 83 | return cls(filename=error.filename, code=error.code) 84 | 85 | def matches(self, prefix: str, filename: str | None) -> bool: 86 | """Determine if this key matches some constraints. 87 | 88 | :param prefix: 89 | The error code prefix that this key's error code should start with. 90 | :param filename: 91 | The filename that we potentially want to match on. This can be 92 | None to only match on error prefix. 93 | :returns: 94 | True if the Key's code starts with the prefix and either filename 95 | is None, or the Key's filename matches the value passed in. 96 | """ 97 | return self.code.startswith(prefix) and ( 98 | filename is None or self.filename == filename 99 | ) 100 | 101 | 102 | class Statistic: 103 | """Simple wrapper around the logic of each statistic. 104 | 105 | Instead of maintaining a simple but potentially hard to reason about 106 | tuple, we create a class which has attributes and a couple 107 | convenience methods on it. 108 | """ 109 | 110 | def __init__( 111 | self, error_code: str, filename: str, message: str, count: int 112 | ) -> None: 113 | """Initialize our Statistic.""" 114 | self.error_code = error_code 115 | self.filename = filename 116 | self.message = message 117 | self.count = count 118 | 119 | @classmethod 120 | def create_from(cls, error: Violation) -> Statistic: 121 | """Create a Statistic from a :class:`flake8.violation.Violation`.""" 122 | return cls( 123 | error_code=error.code, 124 | filename=error.filename, 125 | message=error.text, 126 | count=0, 127 | ) 128 | 129 | def increment(self) -> None: 130 | """Increment the number of times we've seen this error in this file.""" 131 | self.count += 1 132 | -------------------------------------------------------------------------------- /src/flake8/violation.py: -------------------------------------------------------------------------------- 1 | """Contains the Violation error class used internally.""" 2 | from __future__ import annotations 3 | 4 | import functools 5 | import linecache 6 | import logging 7 | from re import Match 8 | from typing import NamedTuple 9 | 10 | from flake8 import defaults 11 | from flake8 import utils 12 | 13 | 14 | LOG = logging.getLogger(__name__) 15 | 16 | 17 | @functools.lru_cache(maxsize=512) 18 | def _find_noqa(physical_line: str) -> Match[str] | None: 19 | return defaults.NOQA_INLINE_REGEXP.search(physical_line) 20 | 21 | 22 | class Violation(NamedTuple): 23 | """Class representing a violation reported by Flake8.""" 24 | 25 | code: str 26 | filename: str 27 | line_number: int 28 | column_number: int 29 | text: str 30 | physical_line: str | None 31 | 32 | def is_inline_ignored(self, disable_noqa: bool) -> bool: 33 | """Determine if a comment has been added to ignore this line. 34 | 35 | :param disable_noqa: 36 | Whether or not users have provided ``--disable-noqa``. 37 | :returns: 38 | True if error is ignored in-line, False otherwise. 39 | """ 40 | physical_line = self.physical_line 41 | # TODO(sigmavirus24): Determine how to handle stdin with linecache 42 | if disable_noqa: 43 | return False 44 | 45 | if physical_line is None: 46 | physical_line = linecache.getline(self.filename, self.line_number) 47 | noqa_match = _find_noqa(physical_line) 48 | if noqa_match is None: 49 | LOG.debug("%r is not inline ignored", self) 50 | return False 51 | 52 | codes_str = noqa_match.groupdict()["codes"] 53 | if codes_str is None: 54 | LOG.debug("%r is ignored by a blanket ``# noqa``", self) 55 | return True 56 | 57 | codes = set(utils.parse_comma_separated_list(codes_str)) 58 | if self.code in codes or self.code.startswith(tuple(codes)): 59 | LOG.debug( 60 | "%r is ignored specifically inline with ``# noqa: %s``", 61 | self, 62 | codes_str, 63 | ) 64 | return True 65 | 66 | LOG.debug( 67 | "%r is not ignored inline with ``# noqa: %s``", self, codes_str 68 | ) 69 | return False 70 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """This is here because mypy doesn't understand PEP 420.""" 2 | from __future__ import annotations 3 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Test configuration for py.test.""" 2 | from __future__ import annotations 3 | 4 | import sys 5 | 6 | import flake8 7 | 8 | flake8.configure_logging(2, "test-logs-%s.%s.log" % sys.version_info[0:2]) 9 | -------------------------------------------------------------------------------- /tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/tests/integration/__init__.py -------------------------------------------------------------------------------- /tests/integration/subdir/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/tests/integration/subdir/__init__.py -------------------------------------------------------------------------------- /tests/integration/subdir/aplugin.py: -------------------------------------------------------------------------------- 1 | """Module that is off sys.path by default, for testing local-plugin-paths.""" 2 | from __future__ import annotations 3 | 4 | 5 | class ExtensionTestPlugin2: 6 | """Extension test plugin in its own directory.""" 7 | 8 | def __init__(self, tree): 9 | """Construct an instance of test plugin.""" 10 | 11 | def run(self): 12 | """Do nothing.""" 13 | -------------------------------------------------------------------------------- /tests/integration/test_aggregator.py: -------------------------------------------------------------------------------- 1 | """Test aggregation of config files and command-line options.""" 2 | from __future__ import annotations 3 | 4 | import os 5 | 6 | import pytest 7 | 8 | from flake8.main import options 9 | from flake8.options import aggregator 10 | from flake8.options import config 11 | from flake8.options import manager 12 | 13 | 14 | @pytest.fixture 15 | def optmanager(): 16 | """Create a new OptionManager.""" 17 | option_manager = manager.OptionManager( 18 | version="3.0.0", 19 | plugin_versions="", 20 | parents=[], 21 | formatter_names=[], 22 | ) 23 | options.register_default_options(option_manager) 24 | return option_manager 25 | 26 | 27 | @pytest.fixture 28 | def flake8_config(tmp_path): 29 | cfg_s = """\ 30 | [flake8] 31 | ignore = 32 | E123, 33 | W234, 34 | E111 35 | exclude = 36 | foo/, 37 | bar/, 38 | bogus/ 39 | quiet = 1 40 | """ 41 | cfg = tmp_path.joinpath("tox.ini") 42 | cfg.write_text(cfg_s) 43 | return str(cfg) 44 | 45 | 46 | def test_aggregate_options_with_config(optmanager, flake8_config): 47 | """Verify we aggregate options and config values appropriately.""" 48 | arguments = [ 49 | "flake8", 50 | "--select", 51 | "E11,E34,E402,W,F", 52 | "--exclude", 53 | "tests/*", 54 | ] 55 | cfg, cfg_dir = config.load_config(flake8_config, []) 56 | options = aggregator.aggregate_options( 57 | optmanager, 58 | cfg, 59 | cfg_dir, 60 | arguments, 61 | ) 62 | 63 | assert options.select == ["E11", "E34", "E402", "W", "F"] 64 | assert options.ignore == ["E123", "W234", "E111"] 65 | assert options.exclude == [os.path.abspath("tests/*")] 66 | 67 | 68 | def test_aggregate_options_when_isolated(optmanager, flake8_config): 69 | """Verify we aggregate options and config values appropriately.""" 70 | arguments = [ 71 | "flake8", 72 | "--select", 73 | "E11,E34,E402,W,F", 74 | "--exclude", 75 | "tests/*", 76 | ] 77 | cfg, cfg_dir = config.load_config(flake8_config, [], isolated=True) 78 | optmanager.extend_default_ignore(["E8"]) 79 | options = aggregator.aggregate_options(optmanager, cfg, cfg_dir, arguments) 80 | 81 | assert options.select == ["E11", "E34", "E402", "W", "F"] 82 | assert options.ignore is None 83 | assert options.exclude == [os.path.abspath("tests/*")] 84 | -------------------------------------------------------------------------------- /tests/integration/test_api_legacy.py: -------------------------------------------------------------------------------- 1 | """Integration tests for the legacy api.""" 2 | from __future__ import annotations 3 | 4 | from flake8.api import legacy 5 | 6 | 7 | def test_legacy_api(tmpdir): 8 | """A basic end-to-end test for the legacy api reporting errors.""" 9 | with tmpdir.as_cwd(): 10 | t_py = tmpdir.join("t.py") 11 | t_py.write("import os # unused import\n") 12 | 13 | style_guide = legacy.get_style_guide() 14 | report = style_guide.check_files([t_py.strpath]) 15 | assert report.total_errors == 1 16 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/tests/unit/__init__.py -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | """Shared fixtures between unit tests.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | 6 | import pytest 7 | 8 | 9 | def options_from(**kwargs): 10 | """Generate a Values instances with our kwargs.""" 11 | kwargs.setdefault("hang_closing", True) 12 | kwargs.setdefault("max_line_length", 79) 13 | kwargs.setdefault("max_doc_length", None) 14 | kwargs.setdefault("indent_size", 4) 15 | kwargs.setdefault("verbose", 0) 16 | kwargs.setdefault("stdin_display_name", "stdin") 17 | kwargs.setdefault("disable_noqa", False) 18 | return argparse.Namespace(**kwargs) 19 | 20 | 21 | @pytest.fixture 22 | def default_options(): 23 | """Fixture returning the default options of flake8.""" 24 | return options_from() 25 | -------------------------------------------------------------------------------- /tests/unit/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PyCQA/flake8/23e4005c5501999c29e1e3774de7ed18d1e4e22d/tests/unit/plugins/__init__.py -------------------------------------------------------------------------------- /tests/unit/plugins/pycodestyle_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import importlib.machinery 4 | import importlib.util 5 | import os.path 6 | 7 | import flake8.plugins.pycodestyle 8 | 9 | HERE = os.path.dirname(os.path.abspath(__file__)) 10 | 11 | 12 | def test_up_to_date(): 13 | """Validate that the generated pycodestyle plugin is up to date. 14 | 15 | We generate two "meta" plugins for pycodestyle to avoid calling overhead. 16 | 17 | To regenerate run: 18 | 19 | ./bin/gen-pycodestyle-plugin > src/flake8/plugins/pycodestyle.py 20 | """ 21 | 22 | path = os.path.join(HERE, "../../../bin/gen-pycodestyle-plugin") 23 | name = os.path.basename(path) 24 | loader = importlib.machinery.SourceFileLoader(name, path) 25 | spec = importlib.util.spec_from_loader(loader.name, loader) 26 | assert spec is not None 27 | mod = importlib.util.module_from_spec(spec) 28 | loader.exec_module(mod) 29 | 30 | expected = "".join(f"{line}\n" for line in mod.lines()) 31 | 32 | with open(flake8.plugins.pycodestyle.__file__) as f: 33 | contents = f.read() 34 | 35 | assert contents == expected 36 | -------------------------------------------------------------------------------- /tests/unit/plugins/reporter_test.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import argparse 4 | import importlib.metadata 5 | 6 | import pytest 7 | 8 | from flake8.formatting import default 9 | from flake8.plugins import finder 10 | from flake8.plugins import reporter 11 | 12 | 13 | def _opts(**kwargs): 14 | kwargs.setdefault("quiet", 0) 15 | kwargs.setdefault("color", "never") 16 | kwargs.setdefault("output_file", None) 17 | return argparse.Namespace(**kwargs) 18 | 19 | 20 | @pytest.fixture 21 | def reporters(): 22 | def _plugin(name, cls): 23 | return finder.LoadedPlugin( 24 | finder.Plugin( 25 | "flake8", 26 | "123", 27 | importlib.metadata.EntryPoint( 28 | name, f"{cls.__module__}:{cls.__name__}", "flake8.report" 29 | ), 30 | ), 31 | cls, 32 | {"options": True}, 33 | ) 34 | 35 | return { 36 | "default": _plugin("default", default.Default), 37 | "pylint": _plugin("pylint", default.Pylint), 38 | "quiet-filename": _plugin("quiet-filename", default.FilenameOnly), 39 | "quiet-nothing": _plugin("quiet-nothing", default.Nothing), 40 | } 41 | 42 | 43 | def test_make_formatter_default(reporters): 44 | ret = reporter.make(reporters, _opts(format="default")) 45 | assert isinstance(ret, default.Default) 46 | assert ret.error_format == default.Default.error_format 47 | 48 | 49 | def test_make_formatter_quiet_filename(reporters): 50 | ret = reporter.make(reporters, _opts(format="default", quiet=1)) 51 | assert isinstance(ret, default.FilenameOnly) 52 | 53 | 54 | @pytest.mark.parametrize("quiet", (2, 3)) 55 | def test_make_formatter_very_quiet(reporters, quiet): 56 | ret = reporter.make(reporters, _opts(format="default", quiet=quiet)) 57 | assert isinstance(ret, default.Nothing) 58 | 59 | 60 | def test_make_formatter_custom(reporters): 61 | ret = reporter.make(reporters, _opts(format="pylint")) 62 | assert isinstance(ret, default.Pylint) 63 | 64 | 65 | def test_make_formatter_format_string(reporters, caplog): 66 | ret = reporter.make(reporters, _opts(format="hi %(code)s")) 67 | assert isinstance(ret, default.Default) 68 | assert ret.error_format == "hi %(code)s" 69 | 70 | assert caplog.record_tuples == [ 71 | ( 72 | "flake8.plugins.reporter", 73 | 30, 74 | "'hi %(code)s' is an unknown formatter. Falling back to default.", 75 | ) 76 | ] 77 | -------------------------------------------------------------------------------- /tests/unit/test_application.py: -------------------------------------------------------------------------------- 1 | """Tests for the Application class.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | 6 | import pytest 7 | 8 | from flake8.main import application as app 9 | 10 | 11 | def options(**kwargs): 12 | """Generate argparse.Namespace for our Application.""" 13 | kwargs.setdefault("verbose", 0) 14 | kwargs.setdefault("output_file", None) 15 | kwargs.setdefault("count", False) 16 | kwargs.setdefault("exit_zero", False) 17 | return argparse.Namespace(**kwargs) 18 | 19 | 20 | @pytest.fixture 21 | def application(): 22 | """Create an application.""" 23 | return app.Application() 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "result_count, catastrophic, exit_zero, value", 28 | [ 29 | (0, False, False, 0), 30 | (0, True, False, 1), 31 | (2, False, False, 1), 32 | (2, True, False, 1), 33 | (0, True, True, 1), 34 | (2, False, True, 0), 35 | (2, True, True, 1), 36 | ], 37 | ) 38 | def test_application_exit_code( 39 | result_count, catastrophic, exit_zero, value, application 40 | ): 41 | """Verify Application.exit_code returns the correct value.""" 42 | application.result_count = result_count 43 | application.catastrophic_failure = catastrophic 44 | application.options = options(exit_zero=exit_zero) 45 | 46 | assert application.exit_code() == value 47 | -------------------------------------------------------------------------------- /tests/unit/test_checker_manager.py: -------------------------------------------------------------------------------- 1 | """Tests for the Manager object for FileCheckers.""" 2 | from __future__ import annotations 3 | 4 | import errno 5 | import multiprocessing 6 | from unittest import mock 7 | 8 | import pytest 9 | 10 | from flake8 import checker 11 | from flake8.main.options import JobsArgument 12 | from flake8.plugins import finder 13 | 14 | 15 | def style_guide_mock(): 16 | """Create a mock StyleGuide object.""" 17 | return mock.MagicMock(**{"options.jobs": JobsArgument("4")}) 18 | 19 | 20 | def _parallel_checker_manager(): 21 | """Call Manager.run() and return the number of calls to `run_serial`.""" 22 | style_guide = style_guide_mock() 23 | manager = checker.Manager(style_guide, finder.Checkers([], [], []), []) 24 | # multiple files is needed for parallel mode 25 | manager.filenames = ("file1", "file2") 26 | return manager 27 | 28 | 29 | def test_oserrors_cause_serial_fall_back(): 30 | """Verify that OSErrors will cause the Manager to fallback to serial.""" 31 | err = OSError(errno.ENOSPC, "Ominous message about spaceeeeee") 32 | with mock.patch("_multiprocessing.SemLock", side_effect=err): 33 | manager = _parallel_checker_manager() 34 | with mock.patch.object(manager, "run_serial") as serial: 35 | manager.run() 36 | assert serial.call_count == 1 37 | 38 | 39 | def test_oserrors_are_reraised(): 40 | """Verify that unexpected OSErrors will cause the Manager to reraise.""" 41 | err = OSError(errno.EAGAIN, "Ominous message") 42 | with mock.patch("_multiprocessing.SemLock", side_effect=err): 43 | manager = _parallel_checker_manager() 44 | with mock.patch.object(manager, "run_serial") as serial: 45 | with pytest.raises(OSError): 46 | manager.run() 47 | assert serial.call_count == 0 48 | 49 | 50 | def test_multiprocessing_cpu_count_not_implemented(): 51 | """Verify that jobs is 0 if cpu_count is unavailable.""" 52 | style_guide = style_guide_mock() 53 | style_guide.options.jobs = JobsArgument("auto") 54 | 55 | with mock.patch.object( 56 | multiprocessing, 57 | "cpu_count", 58 | side_effect=NotImplementedError, 59 | ): 60 | manager = checker.Manager(style_guide, finder.Checkers([], [], []), []) 61 | assert manager.jobs == 0 62 | 63 | 64 | def test_jobs_count_limited_to_file_count(): 65 | style_guide = style_guide_mock() 66 | style_guide.options.jobs = JobsArgument("4") 67 | style_guide.options.filenames = ["file1", "file2"] 68 | manager = checker.Manager(style_guide, finder.Checkers([], [], []), []) 69 | assert manager.jobs == 4 70 | manager.start() 71 | assert manager.jobs == 2 72 | 73 | 74 | def test_make_checkers(): 75 | """Verify that we create a list of FileChecker instances.""" 76 | style_guide = style_guide_mock() 77 | style_guide.options.filenames = ["file1", "file2"] 78 | manager = checker.Manager(style_guide, finder.Checkers([], [], []), []) 79 | manager.start() 80 | assert manager.filenames == ("file1", "file2") 81 | -------------------------------------------------------------------------------- /tests/unit/test_debug.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import importlib.metadata 4 | from unittest import mock 5 | 6 | from flake8.main import debug 7 | from flake8.plugins import finder 8 | 9 | 10 | def test_debug_information(): 11 | def _plugin(pkg, version, ep_name): 12 | return finder.LoadedPlugin( 13 | finder.Plugin( 14 | pkg, 15 | version, 16 | importlib.metadata.EntryPoint( 17 | ep_name, "dne:dne", "flake8.extension" 18 | ), 19 | ), 20 | None, 21 | {}, 22 | ) 23 | 24 | plugins = finder.Plugins( 25 | checkers=finder.Checkers( 26 | tree=[ 27 | _plugin("pkg1", "1.2.3", "X1"), 28 | _plugin("pkg1", "1.2.3", "X2"), 29 | _plugin("pkg2", "4.5.6", "X3"), 30 | ], 31 | logical_line=[], 32 | physical_line=[], 33 | ), 34 | reporters={}, 35 | disabled=[], 36 | ) 37 | 38 | info = debug.information("9001", plugins) 39 | assert info == { 40 | "version": "9001", 41 | "plugins": [ 42 | {"plugin": "pkg1", "version": "1.2.3"}, 43 | {"plugin": "pkg2", "version": "4.5.6"}, 44 | ], 45 | "platform": { 46 | "python_implementation": mock.ANY, 47 | "python_version": mock.ANY, 48 | "system": mock.ANY, 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /tests/unit/test_defaults.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import pytest 4 | 5 | from flake8.defaults import VALID_CODE_PREFIX 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "s", 10 | ( 11 | "E", 12 | "E1", 13 | "E123", 14 | "ABC", 15 | "ABC1", 16 | "ABC123", 17 | ), 18 | ) 19 | def test_valid_plugin_prefixes(s): 20 | assert VALID_CODE_PREFIX.match(s) 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "s", 25 | ( 26 | "", 27 | "A1234", 28 | "ABCD", 29 | "abc", 30 | "a-b", 31 | "☃", 32 | "A𝟗", 33 | ), 34 | ) 35 | def test_invalid_plugin_prefixes(s): 36 | assert VALID_CODE_PREFIX.match(s) is None 37 | -------------------------------------------------------------------------------- /tests/unit/test_exceptions.py: -------------------------------------------------------------------------------- 1 | """Tests for the flake8.exceptions module.""" 2 | from __future__ import annotations 3 | 4 | import pickle 5 | 6 | import pytest 7 | 8 | from flake8 import exceptions 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "err", 13 | ( 14 | exceptions.FailedToLoadPlugin( 15 | plugin_name="plugin_name", 16 | exception=ValueError("boom!"), 17 | ), 18 | exceptions.PluginRequestedUnknownParameters( 19 | plugin_name="plugin_name", 20 | exception=ValueError("boom!"), 21 | ), 22 | exceptions.PluginExecutionFailed( 23 | filename="filename.py", 24 | plugin_name="plugin_name", 25 | exception=ValueError("boom!"), 26 | ), 27 | ), 28 | ) 29 | def test_pickleable(err): 30 | """Ensure that our exceptions can cross pickle boundaries.""" 31 | for proto in range(pickle.HIGHEST_PROTOCOL + 1): 32 | new_err = pickle.loads(pickle.dumps(err, protocol=proto)) 33 | assert str(err) == str(new_err) 34 | orig_e = err.original_exception 35 | new_e = new_err.original_exception 36 | assert (type(orig_e), orig_e.args) == (type(new_e), new_e.args) 37 | -------------------------------------------------------------------------------- /tests/unit/test_file_checker.py: -------------------------------------------------------------------------------- 1 | """Unit tests for the FileChecker class.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | import importlib.metadata 6 | from unittest import mock 7 | 8 | import pytest 9 | 10 | import flake8 11 | from flake8 import checker 12 | from flake8.plugins import finder 13 | 14 | 15 | @mock.patch("flake8.checker.FileChecker._make_processor", return_value=None) 16 | def test_repr(*args): 17 | """Verify we generate a correct repr.""" 18 | file_checker = checker.FileChecker( 19 | filename="example.py", 20 | plugins=finder.Checkers([], [], []), 21 | options=argparse.Namespace(), 22 | ) 23 | assert repr(file_checker) == "FileChecker for example.py" 24 | 25 | 26 | def test_nonexistent_file(): 27 | """Verify that checking non-existent file results in an error.""" 28 | c = checker.FileChecker( 29 | filename="example.py", 30 | plugins=finder.Checkers([], [], []), 31 | options=argparse.Namespace(), 32 | ) 33 | 34 | assert c.processor is None 35 | assert not c.should_process 36 | assert len(c.results) == 1 37 | error = c.results[0] 38 | assert error[0] == "E902" 39 | 40 | 41 | def test_raises_exception_on_failed_plugin(tmp_path, default_options): 42 | """Checks that a failing plugin results in PluginExecutionFailed.""" 43 | fname = tmp_path.joinpath("t.py") 44 | fname.touch() 45 | plugin = finder.LoadedPlugin( 46 | finder.Plugin( 47 | "plugin-name", 48 | "1.2.3", 49 | importlib.metadata.EntryPoint("X", "dne:dne", "flake8.extension"), 50 | ), 51 | mock.Mock(side_effect=ValueError), 52 | {}, 53 | ) 54 | fchecker = checker.FileChecker( 55 | filename=str(fname), 56 | plugins=finder.Checkers([], [], []), 57 | options=default_options, 58 | ) 59 | with pytest.raises(flake8.exceptions.PluginExecutionFailed) as excinfo: 60 | fchecker.run_check(plugin) 61 | expected = ( 62 | f'{fname}: "plugin-name[X]" failed during execution ' 63 | f"due to ValueError()" 64 | ) 65 | assert str(excinfo.value) == expected 66 | -------------------------------------------------------------------------------- /tests/unit/test_filenameonly_formatter.py: -------------------------------------------------------------------------------- 1 | """Tests for the FilenameOnly formatter object.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | 6 | from flake8.formatting import default 7 | from flake8.violation import Violation 8 | 9 | 10 | def options(**kwargs): 11 | """Create an argparse.Namespace instance.""" 12 | kwargs.setdefault("color", "auto") 13 | kwargs.setdefault("output_file", None) 14 | kwargs.setdefault("tee", False) 15 | return argparse.Namespace(**kwargs) 16 | 17 | 18 | def test_caches_filenames_already_printed(): 19 | """Verify we cache filenames when we format them.""" 20 | formatter = default.FilenameOnly(options()) 21 | assert formatter.filenames_already_printed == set() 22 | 23 | formatter.format(Violation("code", "file.py", 1, 1, "text", "l")) 24 | assert formatter.filenames_already_printed == {"file.py"} 25 | 26 | 27 | def test_only_returns_a_string_once_from_format(): 28 | """Verify format ignores the second error with the same filename.""" 29 | formatter = default.FilenameOnly(options()) 30 | error = Violation("code", "file.py", 1, 1, "text", "1") 31 | 32 | assert formatter.format(error) == "file.py" 33 | assert formatter.format(error) is None 34 | 35 | 36 | def test_show_source_returns_nothing(): 37 | """Verify show_source returns nothing.""" 38 | formatter = default.FilenameOnly(options()) 39 | error = Violation("code", "file.py", 1, 1, "text", "1") 40 | 41 | assert formatter.show_source(error) is None 42 | -------------------------------------------------------------------------------- /tests/unit/test_legacy_api.py: -------------------------------------------------------------------------------- 1 | """Tests for Flake8's legacy API.""" 2 | from __future__ import annotations 3 | 4 | from unittest import mock 5 | 6 | import pytest 7 | 8 | from flake8.api import legacy as api 9 | from flake8.formatting import base as formatter 10 | 11 | 12 | def test_styleguide_options(): 13 | """Show that we proxy the StyleGuide.options attribute.""" 14 | app = mock.Mock() 15 | app.options = "options" 16 | style_guide = api.StyleGuide(app) 17 | assert style_guide.options == "options" 18 | 19 | 20 | def test_styleguide_paths(): 21 | """Show that we proxy the StyleGuide.paths attribute.""" 22 | app = mock.Mock() 23 | app.options.filenames = ["paths"] 24 | style_guide = api.StyleGuide(app) 25 | assert style_guide.paths == ["paths"] 26 | 27 | 28 | def test_styleguide_check_files(): 29 | """Verify we call the right application methods.""" 30 | paths = ["foo", "bar"] 31 | app = mock.Mock() 32 | style_guide = api.StyleGuide(app) 33 | report = style_guide.check_files(paths) 34 | 35 | assert app.options.filenames == paths 36 | app.run_checks.assert_called_once_with() 37 | app.report_errors.assert_called_once_with() 38 | assert isinstance(report, api.Report) 39 | 40 | 41 | def test_styleguide_excluded(): 42 | """Verify we delegate to our file checker manager. 43 | 44 | When we add the parent argument, we don't check that is_path_excluded was 45 | called only once. 46 | """ 47 | style_guide = api.get_style_guide(exclude=["file*", "*/parent/*"]) 48 | assert not style_guide.excluded("unrelated.py") 49 | assert style_guide.excluded("file.py") 50 | assert style_guide.excluded("test.py", "parent") 51 | 52 | 53 | def test_styleguide_init_report_does_nothing(): 54 | """Verify if we use None that we don't call anything.""" 55 | app = mock.Mock() 56 | style_guide = api.StyleGuide(app) 57 | style_guide.init_report() 58 | assert app.make_formatter.called is False 59 | assert app.make_guide.called is False 60 | 61 | 62 | def test_styleguide_init_report_with_non_subclass(): 63 | """Verify we raise a ValueError with non BaseFormatter subclasses.""" 64 | app = mock.Mock() 65 | style_guide = api.StyleGuide(app) 66 | with pytest.raises(ValueError): 67 | style_guide.init_report(object) # type: ignore 68 | assert app.make_formatter.called is False 69 | assert app.make_guide.called is False 70 | 71 | 72 | def test_styleguide_init_report(): 73 | """Verify we do the right incantation for the Application.""" 74 | app = mock.Mock(guide="fake") 75 | style_guide = api.StyleGuide(app) 76 | 77 | class FakeFormatter(formatter.BaseFormatter): 78 | def format(self, *args): 79 | raise NotImplementedError 80 | 81 | style_guide.init_report(FakeFormatter) 82 | assert isinstance(app.formatter, FakeFormatter) 83 | assert app.guide is None 84 | app.make_guide.assert_called_once_with() 85 | 86 | 87 | def test_styleguide_input_file(): 88 | """Verify we call StyleGuide.check_files with the filename.""" 89 | app = mock.Mock() 90 | style_guide = api.StyleGuide(app) 91 | with mock.patch.object(style_guide, "check_files") as check_files: 92 | style_guide.input_file("file.py") 93 | check_files.assert_called_once_with(["file.py"]) 94 | 95 | 96 | def test_report_total_errors(): 97 | """Verify total errors is just a proxy attribute.""" 98 | app = mock.Mock(result_count="Fake count") 99 | report = api.Report(app) 100 | assert report.total_errors == "Fake count" 101 | 102 | 103 | def test_report_get_statistics(): 104 | """Verify that we use the statistics object.""" 105 | stats = mock.Mock() 106 | stats.statistics_for.return_value = [] 107 | style_guide = mock.Mock(stats=stats) 108 | app = mock.Mock(guide=style_guide) 109 | 110 | report = api.Report(app) 111 | assert report.get_statistics("E") == [] 112 | stats.statistics_for.assert_called_once_with("E") 113 | -------------------------------------------------------------------------------- /tests/unit/test_main_options.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from flake8.main import options 4 | 5 | 6 | def test_stage1_arg_parser(): 7 | stage1_parser = options.stage1_arg_parser() 8 | opts, args = stage1_parser.parse_known_args( 9 | ["--foo", "--verbose", "src", "setup.py", "--statistics", "--version"] 10 | ) 11 | 12 | assert opts.verbose 13 | assert args == ["--foo", "src", "setup.py", "--statistics", "--version"] 14 | 15 | 16 | def test_stage1_arg_parser_ignores_help(): 17 | stage1_parser = options.stage1_arg_parser() 18 | _, args = stage1_parser.parse_known_args(["--help", "-h"]) 19 | assert args == ["--help", "-h"] 20 | -------------------------------------------------------------------------------- /tests/unit/test_nothing_formatter.py: -------------------------------------------------------------------------------- 1 | """Tests for the Nothing formatter obbject.""" 2 | from __future__ import annotations 3 | 4 | import argparse 5 | 6 | from flake8.formatting import default 7 | from flake8.violation import Violation 8 | 9 | 10 | def options(**kwargs): 11 | """Create an argparse.Namespace instance.""" 12 | kwargs.setdefault("color", "auto") 13 | kwargs.setdefault("output_file", None) 14 | kwargs.setdefault("tee", False) 15 | return argparse.Namespace(**kwargs) 16 | 17 | 18 | def test_format_returns_nothing(): 19 | """Verify Nothing.format returns None.""" 20 | formatter = default.Nothing(options()) 21 | error = Violation("code", "file.py", 1, 1, "text", "1") 22 | 23 | assert formatter.format(error) is None 24 | 25 | 26 | def test_show_source_returns_nothing(): 27 | """Verify Nothing.show_source returns None.""" 28 | formatter = default.Nothing(options()) 29 | error = Violation("code", "file.py", 1, 1, "text", "1") 30 | 31 | assert formatter.show_source(error) is None 32 | -------------------------------------------------------------------------------- /tests/unit/test_option.py: -------------------------------------------------------------------------------- 1 | """Unit tests for flake8.options.manager.Option.""" 2 | from __future__ import annotations 3 | 4 | import functools 5 | from unittest import mock 6 | 7 | import pytest 8 | 9 | from flake8.options import manager 10 | 11 | 12 | def test_to_argparse(): 13 | """Test conversion to an argparse arguments.""" 14 | opt = manager.Option( 15 | short_option_name="-t", 16 | long_option_name="--test", 17 | action="count", 18 | parse_from_config=True, 19 | normalize_paths=True, 20 | ) 21 | assert opt.normalize_paths is True 22 | assert opt.parse_from_config is True 23 | 24 | args, kwargs = opt.to_argparse() 25 | assert args == ["-t", "--test"] 26 | assert kwargs == {"action": "count", "type": mock.ANY} 27 | assert isinstance(kwargs["type"], functools.partial) 28 | 29 | 30 | def test_to_argparse_creates_an_option_as_we_expect(): 31 | """Show that we pass all keyword args to argparse.""" 32 | opt = manager.Option("-t", "--test", action="count") 33 | args, kwargs = opt.to_argparse() 34 | assert args == ["-t", "--test"] 35 | assert kwargs == {"action": "count"} 36 | 37 | 38 | def test_config_name_generation(): 39 | """Show that we generate the config name deterministically.""" 40 | opt = manager.Option( 41 | long_option_name="--some-very-long-option-name", 42 | parse_from_config=True, 43 | ) 44 | 45 | assert opt.config_name == "some_very_long_option_name" 46 | 47 | 48 | def test_config_name_needs_long_option_name(): 49 | """Show that we error out if the Option should be parsed from config.""" 50 | with pytest.raises(ValueError): 51 | manager.Option("-s", parse_from_config=True) 52 | 53 | 54 | def test_dest_is_not_overridden(): 55 | """Show that we do not override custom destinations.""" 56 | opt = manager.Option("-s", "--short", dest="something_not_short") 57 | assert opt.dest == "something_not_short" 58 | -------------------------------------------------------------------------------- /tests/unit/test_pyflakes_codes.py: -------------------------------------------------------------------------------- 1 | """Tests of pyflakes monkey patches.""" 2 | from __future__ import annotations 3 | 4 | import ast 5 | 6 | import pyflakes 7 | 8 | from flake8.plugins import pyflakes as pyflakes_shim 9 | 10 | 11 | def test_all_pyflakes_messages_have_flake8_codes_assigned(): 12 | """Verify all PyFlakes messages have error codes assigned.""" 13 | messages = { 14 | name 15 | for name, obj in vars(pyflakes.messages).items() 16 | if name[0].isupper() and obj.message 17 | } 18 | assert messages == set(pyflakes_shim.FLAKE8_PYFLAKES_CODES) 19 | 20 | 21 | def test_undefined_local_code(): 22 | """In pyflakes 2.1.0 this code's string formatting was changed.""" 23 | src = """\ 24 | import sys 25 | 26 | def f(): 27 | sys = sys 28 | """ 29 | tree = ast.parse(src) 30 | checker = pyflakes_shim.FlakesChecker(tree, "t.py") 31 | message_texts = [s for _, _, s, _ in checker.run()] 32 | assert message_texts == [ 33 | "F823 local variable 'sys' defined in enclosing scope on line 1 referenced before assignment", # noqa: E501 34 | "F841 local variable 'sys' is assigned to but never used", 35 | ] 36 | -------------------------------------------------------------------------------- /tests/unit/test_statistics.py: -------------------------------------------------------------------------------- 1 | """Tests for the statistics module in Flake8.""" 2 | from __future__ import annotations 3 | 4 | import pytest 5 | 6 | from flake8 import statistics as stats 7 | from flake8.violation import Violation 8 | 9 | DEFAULT_ERROR_CODE = "E100" 10 | DEFAULT_FILENAME = "file.py" 11 | DEFAULT_TEXT = "Default text" 12 | 13 | 14 | def make_error(**kwargs): 15 | """Create errors with a bunch of default values.""" 16 | kwargs.setdefault("code", DEFAULT_ERROR_CODE) 17 | kwargs.setdefault("filename", DEFAULT_FILENAME) 18 | kwargs.setdefault("line_number", 1) 19 | kwargs.setdefault("column_number", 1) 20 | kwargs.setdefault("text", DEFAULT_TEXT) 21 | return Violation(**kwargs, physical_line=None) 22 | 23 | 24 | def test_key_creation(): 25 | """Verify how we create Keys from Errors.""" 26 | key = stats.Key.create_from(make_error()) 27 | assert key == (DEFAULT_FILENAME, DEFAULT_ERROR_CODE) 28 | assert key.filename == DEFAULT_FILENAME 29 | assert key.code == DEFAULT_ERROR_CODE 30 | 31 | 32 | @pytest.mark.parametrize( 33 | "code, filename, args, expected_result", 34 | [ 35 | # Error prefix matches 36 | ("E123", "file000.py", ("E", None), True), 37 | ("E123", "file000.py", ("E1", None), True), 38 | ("E123", "file000.py", ("E12", None), True), 39 | ("E123", "file000.py", ("E123", None), True), 40 | # Error prefix and filename match 41 | ("E123", "file000.py", ("E", "file000.py"), True), 42 | ("E123", "file000.py", ("E1", "file000.py"), True), 43 | ("E123", "file000.py", ("E12", "file000.py"), True), 44 | ("E123", "file000.py", ("E123", "file000.py"), True), 45 | # Error prefix does not match 46 | ("E123", "file000.py", ("W", None), False), 47 | # Error prefix matches but filename does not 48 | ("E123", "file000.py", ("E", "file001.py"), False), 49 | # Error prefix does not match but filename does 50 | ("E123", "file000.py", ("W", "file000.py"), False), 51 | # Neither error prefix match nor filename 52 | ("E123", "file000.py", ("W", "file001.py"), False), 53 | ], 54 | ) 55 | def test_key_matching(code, filename, args, expected_result): 56 | """Verify Key#matches behaves as we expect with fthe above input.""" 57 | key = stats.Key.create_from(make_error(code=code, filename=filename)) 58 | assert key.matches(*args) is expected_result 59 | 60 | 61 | def test_statistic_creation(): 62 | """Verify how we create Statistic objects from Errors.""" 63 | stat = stats.Statistic.create_from(make_error()) 64 | assert stat.error_code == DEFAULT_ERROR_CODE 65 | assert stat.message == DEFAULT_TEXT 66 | assert stat.filename == DEFAULT_FILENAME 67 | assert stat.count == 0 68 | 69 | 70 | def test_statistic_increment(): 71 | """Verify we update the count.""" 72 | stat = stats.Statistic.create_from(make_error()) 73 | assert stat.count == 0 74 | stat.increment() 75 | assert stat.count == 1 76 | 77 | 78 | def test_recording_statistics(): 79 | """Verify that we appropriately create a new Statistic and store it.""" 80 | aggregator = stats.Statistics() 81 | assert list(aggregator.statistics_for("E")) == [] 82 | aggregator.record(make_error()) 83 | storage = aggregator._store 84 | for key, value in storage.items(): 85 | assert isinstance(key, stats.Key) 86 | assert isinstance(value, stats.Statistic) 87 | 88 | assert storage[stats.Key(DEFAULT_FILENAME, DEFAULT_ERROR_CODE)].count == 1 89 | 90 | 91 | def test_statistics_for_single_record(): 92 | """Show we can retrieve the only statistic recorded.""" 93 | aggregator = stats.Statistics() 94 | assert list(aggregator.statistics_for("E")) == [] 95 | aggregator.record(make_error()) 96 | statistics = list(aggregator.statistics_for("E")) 97 | assert len(statistics) == 1 98 | assert isinstance(statistics[0], stats.Statistic) 99 | 100 | 101 | def test_statistics_for_filters_by_filename(): 102 | """Show we can retrieve the only statistic recorded.""" 103 | aggregator = stats.Statistics() 104 | assert list(aggregator.statistics_for("E")) == [] 105 | aggregator.record(make_error()) 106 | aggregator.record(make_error(filename="example.py")) 107 | 108 | statistics = list(aggregator.statistics_for("E", DEFAULT_FILENAME)) 109 | assert len(statistics) == 1 110 | assert isinstance(statistics[0], stats.Statistic) 111 | 112 | 113 | def test_statistic_for_retrieves_more_than_one_value(): 114 | """Show this works for more than a couple statistic values.""" 115 | aggregator = stats.Statistics() 116 | for i in range(50): 117 | aggregator.record(make_error(code=f"E1{i:02d}")) 118 | aggregator.record(make_error(code=f"W2{i:02d}")) 119 | 120 | statistics = list(aggregator.statistics_for("E")) 121 | assert len(statistics) == 50 122 | 123 | statistics = list(aggregator.statistics_for("W22")) 124 | assert len(statistics) == 10 125 | -------------------------------------------------------------------------------- /tests/unit/test_violation.py: -------------------------------------------------------------------------------- 1 | """Tests for the flake8.violation.Violation class.""" 2 | from __future__ import annotations 3 | 4 | from unittest import mock 5 | 6 | import pytest 7 | 8 | from flake8.violation import Violation 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "error_code,physical_line,expected_result", 13 | [ 14 | ("E111", "a = 1", False), 15 | ("E121", "a = 1 # noqa: E111", False), 16 | ("E121", "a = 1 # noqa: E111,W123,F821", False), 17 | ("E111", "a = 1 # noqa: E111,W123,F821", True), 18 | ("W123", "a = 1 # noqa: E111,W123,F821", True), 19 | ("W123", "a = 1 # noqa: E111, W123,F821", True), 20 | ("E111", "a = 1 # noqa: E11,W123,F821", True), 21 | ("E121", "a = 1 # noqa:E111,W123,F821", False), 22 | ("E111", "a = 1 # noqa:E111,W123,F821", True), 23 | ("W123", "a = 1 # noqa:E111,W123,F821", True), 24 | ("W123", "a = 1 # noqa:E111, W123,F821", True), 25 | ("E111", "a = 1 # noqa:E11,W123,F821", True), 26 | ("E111", "a = 1 # noqa, analysis:ignore", True), 27 | ("E111", "a = 1 # noqa analysis:ignore", True), 28 | ("E111", "a = 1 # noqa - We do not care", True), 29 | ("E111", "a = 1 # noqa: We do not care", True), 30 | ("E111", "a = 1 # noqa:We do not care", True), 31 | ("ABC123", "a = 1 # noqa: ABC123", True), 32 | ("E111", "a = 1 # noqa: ABC123", False), 33 | ("ABC123", "a = 1 # noqa: ABC124", False), 34 | ], 35 | ) 36 | def test_is_inline_ignored(error_code, physical_line, expected_result): 37 | """Verify that we detect inline usage of ``# noqa``.""" 38 | error = Violation(error_code, "filename.py", 1, 1, "error text", None) 39 | # We want `None` to be passed as the physical line so we actually use our 40 | # monkey-patched linecache.getline value. 41 | 42 | with mock.patch("linecache.getline", return_value=physical_line): 43 | assert error.is_inline_ignored(False) is expected_result 44 | 45 | 46 | def test_disable_is_inline_ignored(): 47 | """Verify that is_inline_ignored exits immediately if disabling NoQA.""" 48 | error = Violation("E121", "filename.py", 1, 1, "error text", "line") 49 | 50 | with mock.patch("linecache.getline") as getline: 51 | assert error.is_inline_ignored(True) is False 52 | 53 | assert getline.called is False 54 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion=2.3.1 3 | envlist = py,flake8,linters,docs 4 | 5 | [testenv] 6 | deps = 7 | pytest!=3.0.5,!=5.2.3 8 | coverage>=6 9 | covdefaults 10 | commands = 11 | coverage run -m pytest {posargs} 12 | coverage report 13 | # ensure 100% coverage of tests 14 | coverage report --fail-under 100 --include tests/* 15 | 16 | # Dogfood our current main version 17 | [testenv:dogfood] 18 | skip_install = true 19 | deps = 20 | wheel 21 | commands = 22 | python setup.py -qq bdist_wheel 23 | pip install --force-reinstall -U --pre --find-links ./dist/ flake8 24 | flake8 --version 25 | flake8 src/flake8/ tests/ setup.py 26 | 27 | # Linters 28 | [testenv:flake8] 29 | skip_install = true 30 | deps = 31 | flake8 32 | flake8-bugbear 33 | flake8-docstrings>=1.3.1 34 | flake8-typing-imports>=1.1 35 | pep8-naming 36 | commands = 37 | flake8 src/flake8/ tests/ setup.py 38 | 39 | [testenv:pylint] 40 | skip_install = true 41 | deps = 42 | pyflakes 43 | pylint!=2.5.0 44 | commands = 45 | pylint src/flake8 46 | 47 | [testenv:doc8] 48 | skip_install = true 49 | deps = 50 | sphinx 51 | doc8 52 | commands = 53 | doc8 docs/source/ 54 | 55 | [testenv:pre-commit] 56 | skip_install = true 57 | deps = pre-commit 58 | commands = 59 | pre-commit run --all-files --show-diff-on-failure 60 | 61 | [testenv:bandit] 62 | skip_install = true 63 | deps = 64 | bandit 65 | commands = 66 | bandit -r src/flake8/ -c .bandit.yml 67 | 68 | [testenv:linters] 69 | skip_install = true 70 | deps = 71 | {[testenv:flake8]deps} 72 | {[testenv:pylint]deps} 73 | {[testenv:doc8]deps} 74 | {[testenv:readme]deps} 75 | {[testenv:bandit]deps} 76 | commands = 77 | {[testenv:flake8]commands} 78 | {[testenv:pylint]commands} 79 | {[testenv:doc8]commands} 80 | {[testenv:readme]commands} 81 | {[testenv:bandit]commands} 82 | 83 | # Documentation 84 | [testenv:docs] 85 | deps = 86 | -rdocs/source/requirements.txt 87 | commands = 88 | sphinx-build -E -W -c docs/source/ -b html docs/source/ docs/build/html 89 | 90 | [testenv:serve-docs] 91 | skip_install = true 92 | changedir = docs/build/html 93 | deps = 94 | commands = 95 | python -m http.server {posargs} 96 | 97 | [testenv:readme] 98 | deps = 99 | readme_renderer 100 | commands = 101 | python setup.py check -r -s 102 | 103 | # Release tooling 104 | [testenv:build] 105 | skip_install = true 106 | deps = 107 | wheel 108 | setuptools 109 | commands = 110 | python setup.py -q sdist bdist_wheel 111 | 112 | [testenv:release] 113 | skip_install = true 114 | deps = 115 | {[testenv:build]deps} 116 | twine >= 1.5.0 117 | commands = 118 | {[testenv:build]commands} 119 | twine upload --skip-existing dist/* 120 | 121 | [flake8] 122 | extend-ignore = E203 123 | per-file-ignores = 124 | src/flake8/formatting/_windows_color.py: N806 125 | tests/*: D 126 | max-complexity = 10 127 | --------------------------------------------------------------------------------