Full Code of m-burst/flake8-pytest-style for AI

master dd7acd396fa2 cached
98 files
147.6 KB
37.6k tokens
257 symbols
1 requests
Download .txt
Repository: m-burst/flake8-pytest-style
Branch: master
Commit: dd7acd396fa2
Files: 98
Total size: 147.6 KB

Directory structure:
gitextract_db9ui74u/

├── .bumpversion.cfg
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug.md
│   │   └── rule-request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── THIRD-PARTY-LICENSES
├── docs/
│   └── rules/
│       ├── PT001.md
│       ├── PT002.md
│       ├── PT003.md
│       ├── PT004.md
│       ├── PT005.md
│       ├── PT006.md
│       ├── PT007.md
│       ├── PT008.md
│       ├── PT009.md
│       ├── PT010.md
│       ├── PT011.md
│       ├── PT012.md
│       ├── PT013.md
│       ├── PT014.md
│       ├── PT015.md
│       ├── PT016.md
│       ├── PT017.md
│       ├── PT018.md
│       ├── PT019.md
│       ├── PT020.md
│       ├── PT021.md
│       ├── PT022.md
│       ├── PT023.md
│       ├── PT024.md
│       ├── PT025.md
│       ├── PT026.md
│       ├── PT027.md
│       ├── PT028.md
│       ├── PT029.md
│       ├── PT030.md
│       └── PT031.md
├── flake8_pytest_style/
│   ├── __init__.py
│   ├── config.py
│   ├── errors.py
│   ├── plugin.py
│   ├── py.typed
│   ├── utils.py
│   └── visitors/
│       ├── __init__.py
│       ├── assertion.py
│       ├── fail.py
│       ├── fixtures.py
│       ├── imports.py
│       ├── marks.py
│       ├── parametrize.py
│       ├── patch.py
│       ├── raises.py
│       ├── t_functions.py
│       ├── try_except.py
│       └── warns.py
├── pyproject.toml
├── scripts/
│   └── pypi_readme.py
├── setup.cfg
└── tests/
    ├── __init__.py
    ├── conftest.py
    ├── test_PT001_incorrect_fixture_parentheses_style.py
    ├── test_PT002_fixture_positional_args.py
    ├── test_PT003_extraneous_scope_function.py
    ├── test_PT004_missing_fixture_name_underscore.py
    ├── test_PT005_incorrect_fixture_name_underscore.py
    ├── test_PT006_parametrize_names_wrong_type.py
    ├── test_PT007_parametrize_values_wrong_type.py
    ├── test_PT008_patch_with_lambda.py
    ├── test_PT009_unittest_assertion.py
    ├── test_PT010_raises_without_exception.py
    ├── test_PT011_raises_too_broad.py
    ├── test_PT012_raises_with_multiple_statements.py
    ├── test_PT013_incorrect_pytest_import.py
    ├── test_PT014_duplicate_parametrize_test_cases.py
    ├── test_PT015_assert_always_false.py
    ├── test_PT016_fail_without_message.py
    ├── test_PT017_assert_in_except.py
    ├── test_PT018_composite_assertion.py
    ├── test_PT019_fixture_param_without_value.py
    ├── test_PT020_deprecated_yield_fixture.py
    ├── test_PT021_fixture_finalizer_callback.py
    ├── test_PT022_useless_yield_fixture.py
    ├── test_PT023_incorrect_mark_parentheses_style.py
    ├── test_PT024_unncessary_asyncio_mark_on_fixture.py
    ├── test_PT025_erroneous_usefixtures_on_fixture.py
    ├── test_PT026_usefixtures_without_parameters.py
    ├── test_PT027_unittest_raises_assertion.py
    ├── test_PT028_test_function_argument_with_default.py
    ├── test_PT029_warns_without_warning.py
    ├── test_PT030_warns_too_broad.py
    ├── test_PT031_warns_with_multiple_statements.py
    ├── test_config.py
    └── test_plugin.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .bumpversion.cfg
================================================
[bumpversion]
current_version = 2.2.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:README.md]
search = **Unreleased**
replace = **Unreleased**
	
	...
	
	**{new_version} - {now:%Y-%m-%d}**

[bumpversion:file:flake8_pytest_style/plugin.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"


================================================
FILE: .github/ISSUE_TEMPLATE/bug.md
================================================
---
name: Bug
about: Report a bug in the plugin
title: When I have {{ SOMETHING EXPECTED }} in test, {{ AN UNEXPECTED THING }} happens
labels: bug
assignees: ''

---

# Bug report

## What's wrong

<!--
Please describe what is not working. Perhaps the plugin reports a violation when it shouldn't, or vice versa?
Please attach a code sample which reproduces the problem.
-->

## How it should work

<!-- Please describe the expected behaviour of the plugin in the given case. -->

## System information

* Operating system:
* Python version:
* flake8 version:
* flake8-pytest-style version:


================================================
FILE: .github/ISSUE_TEMPLATE/rule-request.md
================================================
---
name: Rule request
about: Suggest a new rule for the plugin
title: I want a rule that will {{ DO SOMETHING AWESOME }}
labels: enhancement, rule request
assignees: ''

---

# Rule request

## Description

<!-- What do you think the plugin should check? Please provide examples of good and bad code. -->

## Rationale

<!-- Why do you think this is a good idea for a rule? -->


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: pip
  directory: "/"
  schedule:
    interval: daily
    time: "02:00"
  open-pull-requests-limit: 10


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
  push:
    branches: [master]
    tags: ['*']
  pull_request:

jobs:
  ci:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version:
          - "3.10"
          - 3.11
          - 3.12
          - 3.13
          - 3.14

    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}

      - uses: snok/install-poetry@v1
        with:
          version: 2.2.1

      - name: Show environment info
        run: |
          python --version
          pip --version
          poetry --version
          poetry config --list

      - name: Setup dependencies cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/pypoetry/virtualenvs
          key: ${{ runner.os }}-${{ matrix.python-version }}-poetry-${{ hashFiles('poetry.lock') }}

      - name: Install dependencies
        run: |
          poetry install
          pip install codecov

      - name: Run lint
        run: make lint

      - name: Run tests
        run: make test

      - name: Upload coverage data
        run: codecov


================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
  workflow_dispatch:

jobs:
  release:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version:
          - 3.14

    steps:
      - uses: actions/checkout@v5

      - uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}

      - uses: snok/install-poetry@v1
        with:
          version: 2.2.1

      - name: Setup dependencies cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/pypoetry/virtualenvs
          key: ${{ runner.os }}-${{ matrix.python-version }}-poetry-${{ hashFiles('poetry.lock') }}

      - name: Install dependencies
        run: poetry install

      - name: Build distribution
        run: |
          poetry run ./scripts/pypi_readme.py
          poetry build

      - name: Publish to PyPI
        run: poetry publish --no-interaction
        env:
          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
README-pypi.md

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# IDE
.idea/
.vscode/


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Mikhail Burshteyn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Makefile
================================================
.PHONY: init test test-cov lint format

CODE = flake8_pytest_style
TEST = poetry run pytest --verbosity=2 --showlocals --strict-markers --cov=$(CODE)

init:
	poetry install
	echo '#!/bin/sh\nmake lint test\n' > .git/hooks/pre-commit
	chmod +x .git/hooks/pre-commit

test:
	$(TEST) -k "$(k)"

test-cov:
	$(TEST) --cov-report=html

lint:
	poetry run flake8 --jobs 4 --statistics --show-source $(CODE) tests scripts
	poetry run pylint --jobs 4 --rcfile=setup.cfg $(CODE)
	poetry run mypy $(CODE) tests scripts
	poetry run black --check $(CODE) tests scripts
	poetry run pytest --dead-fixtures --dup-fixtures

format:
	poetry run isort $(CODE) tests scripts
	poetry run black $(CODE) tests scripts

bump_major:
	poetry run bumpversion major

bump_minor:
	poetry run bumpversion minor

bump_patch:
	poetry run bumpversion patch


================================================
FILE: README.md
================================================
# flake8-pytest-style

[![pypi](https://badge.fury.io/py/flake8-pytest-style.svg)](https://pypi.org/project/flake8-pytest-style)
[![Python: 3.10+](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://pypi.org/project/flake8-pytest-style)
[![Downloads](https://img.shields.io/pypi/dm/flake8-pytest-style.svg)](https://pypistats.org/packages/flake8-pytest-style)
[![Build Status](https://github.com/m-burst/flake8-pytest-style/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/m-burst/flake8-pytest-style/actions/workflows/ci.yml)
[![Code coverage](https://codecov.io/gh/m-burst/flake8-pytest-style/branch/master/graph/badge.svg)](https://codecov.io/gh/m-burst/flake8-pytest-style)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://en.wikipedia.org/wiki/MIT_License)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)

## Description

A `flake8` plugin checking common style issues or inconsistencies with `pytest`-based tests.

Currently the following errors are reported:

| Code    | Description |
| ------- | ----------- |
| [PT001] | use @pytest.fixture over @pytest.fixture() <br> (configurable by `pytest-fixture-no-parentheses`) |
| [PT002] | configuration for fixture '{name}' specified via positional args, use kwargs |
| [PT003] | scope='function' is implied in @pytest.fixture() |
| [PT004] | fixture '{name}' does not return anything, add leading underscore |
| [PT005] | fixture '{name}' returns a value, remove leading underscore |
| [PT006] | wrong name(s) type in @pytest.mark.parametrize, expected {expected_type} <br> (configurable by `pytest-parametrize-names-type`) |
| [PT007] | wrong values type in @pytest.mark.parametrize, expected {expected_type} <br> (configurable by `pytest-parametrize-values-type` and `pytest-parametrize-values-row-type`) |
| [PT008] | use return_value= instead of patching with lambda |
| [PT009] | use a regular assert instead of unittest-style '{assertion}' |
| [PT010] | set the expected exception in pytest.raises() |
| [PT011] | pytest.raises({exception}) is too broad, set the match parameter or use a more specific exception <br> (configurable by `pytest-raises-require-match-for`) |
| [PT012] | pytest.raises() block should contain a single simple statement |
| [PT013] | found incorrect import of pytest, use simple 'import pytest' instead |
| [PT014] | found duplicate test cases {indexes} in @pytest.mark.parametrize |
| [PT015] | assertion always fails, replace with pytest.fail() |
| [PT016] | no message passed to pytest.fail() |
| [PT017] | found assertion on exception {name} in except block, use pytest.raises() instead |
| [PT018] | assertion should be broken down into multiple parts |
| [PT019] | fixture {name} without value is injected as parameter, use @pytest.mark.usefixtures instead |
| [PT020] | @pytest.yield_fixture is deprecated, use @pytest.fixture |
| [PT021] | use yield instead of request.addfinalizer |
| [PT022] | no teardown in fixture {name}, use return instead of yield |
| [PT023] | use @pytest.mark.foo over @pytest.mark.foo() <br> (configurable by `pytest-mark-no-parentheses`) |
| [PT024] | pytest.mark.asyncio is unnecessary for fixtures |
| [PT025] | pytest.mark.usefixtures has no effect on fixtures |
| [PT026] | useless pytest.mark.usefixtures without parameters | 
| [PT027] | use pytest.raises() instead of unittest-style '{assertion}' |
| [PT028] | test function {name} has default value for argument {arg}, remove it |
| [PT029] | set the expected warning in pytest.warns() |
| [PT030] | pytest.warns({warning}) is too broad, set the match parameter or use a more specific warning <br> (configurable by `pytest-warns-require-match-for`) |
| [PT031] | pytest.warns() block should contain a single simple statement |

## Installation

    pip install flake8-pytest-style

## Configuration

The plugin has the following configuration options:

* `pytest-fixture-no-parentheses` &mdash; see [PT001]
* `pytest-parametrize-names-type` &mdash; see [PT006]
* `pytest-parametrize-values-type` &mdash; see [PT007]
* `pytest-parametrize-values-row-type` &mdash; see [PT007]
* `pytest-raises-require-match-for` &mdash; see [PT011]
* `pytest-mark-no-parentheses` &mdash; see [PT023]
* `pytest-warns-require-match-for` &mdash; see [PT030]

## For developers

### Install deps and setup pre-commit hook

    make init

### Run linters, autoformat, tests etc.

    make format lint test

### Bump new version

    make bump_major
    make bump_minor
    make bump_patch

## License

MIT

## Change Log

**Unreleased**

...

**2.2.0 - 2025-10-20**

* require at least Python 3.10
* support Python 3.14

**2.1.0 - 2025-01-10**

* support `reason=` kwarg in `pytest.fail` for [PT016]
* add [PT028] (checks for default values in test functions)
* add [PT029] (checks for `pytest.warns` without expected warning)
* add [PT030] (checks for too broad `pytest.warns` clauses)
* add [PT031] (checks for multiple statements in `pytest.warns` blocks)
* require at least Python 3.9
* support Python 3.13

**2.0.0 - 2024-04-01**

* **BREAKING:** invert default values for `pytest-fixture-no-parentheses` and `pytest-mark-no-parentheses`
  to conform with `pytest` official style
  * If you get a lot of [PT001] or [PT023] violations after upgrading, consider setting explicit values
    for these configuration options
* require at least Python 3.8.1
* support Python 3.12

**1.7.2 - 2023-02-15**

* fix false positive for [PT009] on `pytest.fail`

**1.7.1 - 2023-02-15**

* update list of unittest-style assert methods for [PT009]/[PT027]

**1.7.0 - 2023-02-09**

* require at least Python 3.7.2
* support Python 3.11
* add [PT027] (checks for unittest-style `assertRaises`)

**1.6.0 - 2021-12-23**

* require at least Python 3.6.2
* expose `py.typed` file

**1.5.1 - 2021-11-05**

* better wording for [PT011]
* support Python 3.10

**1.5.0 - 2021-06-18**

* add [PT025] (checks for erroneous `pytest.mark.usefixtures` on fixtures)
* add [PT026] (checks for `pytest.mark.usefixtures` without parameters)

**1.4.4 - 2021-06-17**

* fix [PT023] not checking marks in classes
* fix [PT004] incorrectly firing on fixtures with `yield from`

**1.4.2 - 2021-05-24**

* update `flake8-plugin-utils` version to improve stability

**1.4.1 - 2021-04-01**

* fix argparse-related warnings

**1.4.0 - 2021-03-14**

* add [PT023] (checks for parentheses consistency in `pytest.mark` usage)
* add [PT024] (checks for unnecessary `pytest.mark.asyncio` on fixtures)
* fix [PT004], [PT005] firing on abstract fixtures
* fix [PT012] firing on `with` statements containing a single `pass`

**1.3.0 - 2020-08-30**

* add [PT022] (checks for `yield` fixtures without teardown)

**1.2.3 - 2020-08-06**

* update `flake8-plugin-utils` dependency to fix encoding problems on Windows

**1.2.2 - 2020-07-23**

* fix [PT004]/[PT005] inspecting returns of nested functions

**1.2.1 - 2020-06-15**

* fix [PT021] for factory fixtures (#46)

**1.2.0 - 2020-06-12**

* support scoped `mocker` fixtures from `pytest-mock` for [PT008]
* check for positional-only lambda arguments in [PT008]
* add [PT020] (checks for `pytest.yield_fixture`)
* add [PT021] (checks for `request.addfinalizer`)
* add documentation pages for all rules

**1.1.1 - 2020-04-17**

* fix [PT011] not reporting `match=''` as a violation

**1.1.0 - 2020-04-14**

* add [PT015] (checks for `assert False`)
* add [PT016] (checks for `pytest.fail()` without message)
* add [PT017] (checks for assertions on exceptions in `except` blocks)
* add [PT018] (checks for composite assertions)
* add [PT019] (checks for fixtures without value injected as parameters)

**1.0.0 - 2020-03-26**

* add [PT014] (checks for duplicate test cases in `@pytest.mark.parametrize`)

**0.6.0 - 2020-03-21**

* add configuration option `pytest-parametrize-names-type` for [PT006]
* add configuration options `pytest-parametrize-values-type` and
`pytest-parametrize-values-row-type` for [PT007]

**0.5.0 - 2020-03-09**

* add configuration option `pytest-fixture-no-parentheses` for [PT001]
* add [PT013] (checks for `from`-imports from `pytest`)

**0.4.0 - 2020-03-09**

* add [PT012] (checks for multiple statements in `with pytest.raises()`)

**0.3.1 - 2020-03-09**

* fix default value of `pytest-raises-require-match-for` config option

**0.3.0 - 2020-03-09**

* add [PT010] and [PT011] (checks for `pytest.raises` parameters)

**0.2.0 - 2020-03-01**

* add [PT009] (ported from [flake8-pytest](https://github.com/vikingco/flake8-pytest))

**0.1.3 - 2019-05-24**

* add `yield` fixtures support
* fix changelog entry for 0.1.2

**0.1.2 - 2019-05-23**

* fix parametrize checkers not working in decorators

**0.1.1 - 2019-05-23**

* update PyPI description

**0.1.0 - 2019-05-23**

* initial

[PT001]: /docs/rules/PT001.md
[PT002]: /docs/rules/PT002.md
[PT003]: /docs/rules/PT003.md
[PT004]: /docs/rules/PT004.md
[PT005]: /docs/rules/PT005.md
[PT006]: /docs/rules/PT006.md
[PT007]: /docs/rules/PT007.md
[PT008]: /docs/rules/PT008.md
[PT009]: /docs/rules/PT009.md
[PT010]: /docs/rules/PT010.md
[PT011]: /docs/rules/PT011.md
[PT012]: /docs/rules/PT012.md
[PT013]: /docs/rules/PT013.md
[PT014]: /docs/rules/PT014.md
[PT015]: /docs/rules/PT015.md
[PT016]: /docs/rules/PT016.md
[PT017]: /docs/rules/PT017.md
[PT018]: /docs/rules/PT018.md
[PT019]: /docs/rules/PT019.md
[PT020]: /docs/rules/PT020.md
[PT021]: /docs/rules/PT021.md
[PT022]: /docs/rules/PT022.md
[PT023]: /docs/rules/PT023.md
[PT024]: /docs/rules/PT024.md
[PT025]: /docs/rules/PT025.md
[PT026]: /docs/rules/PT026.md
[PT027]: /docs/rules/PT027.md
[PT028]: /docs/rules/PT028.md
[PT029]: /docs/rules/PT029.md
[PT030]: /docs/rules/PT030.md
[PT031]: /docs/rules/PT031.md


================================================
FILE: THIRD-PARTY-LICENSES
================================================
*******************************************************************************
https://github.com/m-burst/cookiecutter-pypackage-poetry
*******************************************************************************

MIT License

Copyright (c) 2019 Afonasev Evgeniy
Copyright (c) 2019 Mikhail Burshteyn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: docs/rules/PT001.md
================================================
# PT001

`use @pytest.fixture over @pytest.fixture()`

## Configuration

* `pytest-fixture-no-parentheses`  
Boolean flag specifying whether `@pytest.fixture()` without parameters
should have parentheses.  
If the option is set to true (the default), `@pytest.fixture` is valid
and `@pytest.fixture()` is an error.  
If set to false, `@pytest.fixture()` is valid and `@pytest.fixture` is
an error.

## Examples

Bad code (assuming `pytest-fixture-no-parentheses` set to true):

```python
import pytest

@pytest.fixture()
def my_fixture():
    ...
```

Good code:

```python
import pytest

@pytest.fixture
def my_fixture():
    ...
```

## Rationale

* to enforce consistency between all fixtures in a codebase


================================================
FILE: docs/rules/PT002.md
================================================
# PT002

`configuration for fixture '{name}' specified via positional args, use kwargs`

## Examples

Bad code:

```python
import pytest

@pytest.fixture('module')
def my_fixture():
    ...
```

Good code:

```python
import pytest

@pytest.fixture(scope='module')
def my_fixture():
    ...
```

## Rationale

* to make parameters meaning more obvious
* to enforce consistency between all fixtures in a codebase


================================================
FILE: docs/rules/PT003.md
================================================
# PT003

`scope='function' is implied in @pytest.fixture()`

Fixtures with function scope should not specify scope explicitly because
function scope is implied by default.

## Examples

Bad code:

```python
import pytest

@pytest.fixture(scope='function')
def my_fixture():
    ...
```

Good code:

```python
import pytest

@pytest.fixture()
def my_fixture():
    ...
```

## Rationale

* to enforce consistency between all fixtures in a codebase


================================================
FILE: docs/rules/PT004.md
================================================
# PT004

`fixture '{name}' does not return anything, add leading underscore`

This rule does not fire on abstract fixtures (those decorated with `@abc.abstractmethod`),
so that only the actual fixture implementations will be checked.

This rule also does not fire on fixtures containing `yield from`, because there is
no reasonable way to determine whether the generator yields a value.

## Examples

Bad code:

```python
import pytest

@pytest.fixture()
def patch_something(mocker):
    mocker.patch('module.object')

@pytest.fixture()
def use_context():
    with create_context():
        yield
```

Good code:

```python
import pytest

@pytest.fixture()
def _patch_something(mocker):
    mocker.patch('module.object')

@pytest.fixture()
def _use_context():
    with create_context():
        yield
```

## Rationale

* to enforce a naming convention for fixtures:
fixtures that don't return a value start with a leading underscore (e.g. `_patch_something`),
fixtures that return a value don't start with a leading underscore (e.g. `some_object`)

See also [PT005](PT005.md).


================================================
FILE: docs/rules/PT005.md
================================================
# PT005

`fixture '{name}' returns a value, remove leading underscore`

This rule does not fire on abstract fixtures (those decorated with `@abc.abstractmethod`),
so that only the actual fixture implementations will be checked.

## Examples

Bad code:

```python
import pytest

@pytest.fixture()
def _some_object():
    return SomeClass()

@pytest.fixture()
def _some_object_with_cleanup():
    obj = SomeClass()
    yield obj
    obj.cleanup()
```

Good code:

```python
import pytest

@pytest.fixture()
def some_object():
    return SomeClass()

@pytest.fixture()
def some_object_with_cleanup():
    obj = SomeClass()
    yield obj
    obj.cleanup()
```

## Rationale

* to enforce a naming convention for fixtures:
fixtures that don't return a value start with a leading underscore (e.g. `_patch_something`),
fixtures that return a value don't start with a leading underscore (e.g. `some_object`)

See also [PT004](PT004.md).


================================================
FILE: docs/rules/PT006.md
================================================
# PT006

`wrong name(s) type in @pytest.mark.parametrize, expected {expected_type}`

For a single name the expected type is always a plain string.
For multiple names the expected type is controlled by the configuration
variable `pytest-parametrize-names-type`.

## Configuration

* `pytest-parametrize-names-type`  
Expected type for multiple argument names in `@pytest.mark.parametrize`.
The following values are supported:
  * `csv` &mdash; a comma-separated list, e.g. `@pytest.mark.parametrize('name1,name2', ...)`
  * `tuple` (default) &mdash; e.g. `@pytest.mark.parametrize(('name1', 'name2'), ...)`
  * `list` &mdash; e.g. `@pytest.mark.parametrize(['name1', 'name2'], ...)`

## Examples

Bad code (assuming `pytest-parametrize-names-type` is set to `tuple`):

```python
import pytest

# single parameter, always expecting string
@pytest.mark.parametrize(('param', ), [1, 2, 3])
def test_foo(param):
    ...

# multiple parameters, expecting tuple due to settings
@pytest.mark.parametrize(['param1', 'param2'], [(1, 2), (3, 4)])
def test_bar(param1, param2):
    ...

# multiple parameters, expecting tuple due to settings
@pytest.mark.parametrize('param1,param2', [(1, 2), (3, 4)])
def test_baz(param1, param2):
    ...
```

Good code:

```python
import pytest

@pytest.mark.parametrize('param', [1, 2, 3])
def test_foo(param):
    ...

@pytest.mark.parametrize(('param1', 'param2'), [(1, 2), (3, 4)])
def test_bar(param1, param2):
    ...
```

## Rationale

* to enforce consistency between all tests in a codebase


================================================
FILE: docs/rules/PT007.md
================================================
# PT007

`wrong values type in @pytest.mark.parametrize, expected {expected_type}`

The expected type of the list of rows is controlled by the configuration
variable `pytest-parametrize-values-type`. The expected type of each row in
case of multiple arguments is controlled by the configuration variable
`pytest-parametrize-values-row-type`.

## Configuration

* `pytest-parametrize-values-type`  
Expected type for the list of values rows in `@pytest.mark.parametrize`.
The following values are supported:
  * `tuple` &mdash; e.g. `@pytest.mark.parametrize('name', (1, 2, 3))`
  * `list` (default) &mdash; e.g. `@pytest.mark.parametrize('name', [1, 2, 3])`

* `pytest-parametrize-values-row-type`  
Expected type for each row of values in `@pytest.mark.parametrize` in case of
multiple parameters. The following values are supported:
  * `tuple` (default) &mdash; e.g. `@pytest.mark.parametrize(('name1', 'name2'), [(1, 2), (3, 4)])`
  * `list` &mdash; e.g. `@pytest.mark.parametrize(('name1', 'name2'), [[1, 2], [3, 4]])`

## Examples

Bad code (assuming `pytest-parametrize-values-type` set to `list` and
`pytest-parametrize-values-row-type` set to `tuple`):

```python
import pytest

# expected list, got tuple
@pytest.mark.parametrize('param', (1, 2))
def test_foo(param):
    ...

# expected top-level list, got tuple
@pytest.mark.parametrize(
    ('param1', 'param2'),
    (
        (1, 2),
        (3, 4),
    ),
)
def test_bar(param1, param2):
    ...

# expected individual rows to be tuples, got lists
@pytest.mark.parametrize(
    ('param1', 'param2'),
    [
        [1, 2],
        [3, 4],
    ],
)
def test_baz(param1, param2):
    ...
```

```python
import pytest

@pytest.mark.parametrize('param', [1, 2])
def test_foo(param):
    ...

@pytest.mark.parametrize(
    ('param1', 'param2'),
    [
        (1, 2),
        (3, 4),
    ],
)
def test_bar(param1, param2):
    ...
```

## Rationale

* to enforce consistency between all tests in a codebase


================================================
FILE: docs/rules/PT008.md
================================================
# PT008

`use return_value= instead of patching with lambda`

This checks calls to `unittest.mock.patch` from standard library,
as well as `mocker`, `session_mocker` etc. fixtures from
[`pytest-mock`](https://pypi.org/project/pytest-mock/) library.

e.g. `mocker.patch('target', return_value=7)` is OK, and `mocker.patch('target', lambda *args: 7)` is an error

## Examples

Bad code:

```python
def test_foo(mocker):
    mocker.patch('module.target', lambda x, y: 7)
```

Good code:

```python
def test_foo(mocker):
    mocker.patch('module.target', return_value=7)
    # if lambda parameters are used, it's not a violation
    mocker.patch('module.other_target', lambda x, y: x)
```

## Rationale

* this style of patching allows to use all mock functionality, such as checking
the list of calls to the patched function:
  ```python
  def test_foo(mocker):
      mock_target = mocker.patch('module.target', return_value=7)
      do_something()
      mock_target.assert_called_once()
  ```


================================================
FILE: docs/rules/PT009.md
================================================
# PT009

`use a regular assert instead of unittest-style '{assertion}'`

## Examples

Bad code:

```python
import unittest

class TestFoo(unittest.TestCase):
    def test_foo(self):
        self.assertEqual(a, b)
```

Good code:

```python
import unittest

class TestFoo(unittest.TestCase):
    def test_foo(self):
        assert a == b
```

## Rationale

* to enforce the assertion style recommended by pytest
* to make use of pytest's assertion rewriting


================================================
FILE: docs/rules/PT010.md
================================================
# PT010

`set the expected exception in pytest.raises()`

## Examples

Bad code:

```python
import pytest

def test_foo():
    with pytest.raises():
        do_something()
```

Good code:

```python
import pytest

def test_foo():
    with pytest.raises(SomeException):
        do_something()
```


## Rationale

* not passing the exception class will fail at runtime


================================================
FILE: docs/rules/PT011.md
================================================
# PT011

`pytest.raises({exception}) is too broad, set the match parameter or use a more specific exception`

## Configuration

* `pytest-raises-require-match-for`  
Comma-separated list of exception names that require a `match=` parameter
in a `pytest.raises()` call. By default the list contains the following
exceptions:
  * `BaseException`, `Exception`
  * `ValueError`
  * `OSError`, `IOError`, `EnvironmentError`, `socket.error`

## Examples

Bad code:

```python
import pytest

def test_foo():
    with pytest.raises(ValueError):
        ...

    # empty string is also an error
    with pytest.raises(ValueError, match=''):
        ...
```

Good code:

```python
import pytest

def test_foo():
    with pytest.raises(ValueError, match='expected message'):
        ...
```

## Rationale

* to help ensure that the `pytest.raises` clause is not too broad


================================================
FILE: docs/rules/PT012.md
================================================
# PT012

`pytest.raises() block should contain a single simple statement`

This forbids multiple statements and control flow structures within
`pytest.raises()` blocks.

## Examples

Bad code:

```python
import pytest

def test_foo():
    with pytest.raises(MyException):
        # if get_object() raises MyException, the test is incorrect
        obj = get_object()
        obj.do_something()

    with pytest.raises(MyException):
        if some_condition:
            do_something()
```

Good code:

```python
import pytest

def test_foo():
    obj = get_object()
    with pytest.raises(MyException):
        obj.do_something()
```

An empty `with`-statement (containing only a `pass` inside) is allowed
in order to allow testing of exceptions raised by context manager enter/exit methods.
```python
import pytest

def test_my_context_manager():
    context_manager = get_context_manager()
    with pytest.raises(MyException):
        with context_manager:
            pass
```

## Rationale

* to help ensure that only the expected exception from code under test is
caught in a `pytest.raises` block (e.g. not an exception from
initialization/finalization code)


================================================
FILE: docs/rules/PT013.md
================================================
# PT013

`found incorrect import of pytest, use simple 'import pytest' instead`

## Examples

Bad code:

```python
import pytest as pt
from pytest import fixture
```

Good code:

```python
import pytest
```

## Rationale

* to enforce consistency
* to help the linter check usage of members imported from `pytest`


================================================
FILE: docs/rules/PT014.md
================================================
# PT014

`found duplicate test cases {indexes} in @pytest.mark.parametrize`

## Examples

Bad code:

```python
import pytest

@pytest.mark.parametrize(
    ('param1', 'param2'),
    [
        (1, 2),
        (1, 2),
    ]
)
def test_foo(param1, param2):
    ...
```

## Rationale

* to help avoid duplicate test cases and accidental mistakes


================================================
FILE: docs/rules/PT015.md
================================================
# PT015

`assertion always fails, replace with pytest.fail()`

## Examples

Bad code:

```python
def test_foo():
    if some_condition:
        assert False, 'some_condition was True'
```

Good code:

```python
import pytest

def test_foo():
    if some_condition:
        pytest.fail('some_condition was True')
```

## Rationale

* to enforce consistency across all tests in a codebase
* to improve readability of test code and error messages


================================================
FILE: docs/rules/PT016.md
================================================
# PT016

`no message passed to pytest.fail()`

The function `pytest.fail` must be called either with a positional argument,
a keyword argument `reason=` (since pytest version 7.0.0), or a keyword argument `msg=`
(for compatibility with older versions of pytest).  Passing a keyword argument under a
wrong name will also be reported as a violation.

## Examples

Bad code:

```python
import pytest

def test_foo():
    pytest.fail()

def test_bar():
    pytest.fail('')

def test_baz():
    pytest.fail(message='...')  # wrong kwarg name
```

Good code:

```python
import pytest

def test_foo():
    pytest.fail('...')

def test_bar():
    pytest.fail(reason='...')

def test_baz():
    pytest.fail(msg='...')
```

## Rationale

* to make it easier to understand and debug test failures


================================================
FILE: docs/rules/PT017.md
================================================
# PT017

`found assertion on exception {name} in except block, use pytest.raises() instead`

## Examples

Bad code:

```python
def test_foo():
    try:
        1 / 0
    except ZeroDivisionError as e:
        assert e.args
```

Good code:
```python
import pytest

def test_foo():
    with pytest.raises(ZeroDivisionError) as e:
        1 / 0
    assert e.value.args
```

## Rationale

* to avoid the situations when the test incorrectly passes because exception
was not raised


================================================
FILE: docs/rules/PT018.md
================================================
# PT018

`assertion should be broken down into multiple parts`

This violation is reported when the plugin encounter an assertion on multiple
conditions.

## Examples

Bad code:

```python
def test_foo():
    assert something and something_else

def test_bar():
    assert not (something or something_else)
```

Good code:

```python
def test_foo():
    assert something
    assert something_else

def test_bar():
    assert not something
    assert not something_else
```

## Rationale

* to make it easier to understand and debug test failures


================================================
FILE: docs/rules/PT019.md
================================================
# PT019

`fixture {name} without value is injected as parameter, use @pytest.mark.usefixtures instead`

## Examples

Bad code:

```python
def test_foo(_patch_something):
    ...
```

Good code:
```python
import pytest

@pytest.mark.usefixtures('_patch_something')
def test_foo():
    ...
```

## Rationale

* to avoid unused parameters in test functions


================================================
FILE: docs/rules/PT020.md
================================================
# PT020

`@pytest.yield_fixture is deprecated, use @pytest.fixture`

## Examples

Bad code:

```python
import pytest

@pytest.yield_fixture()
def my_fixture():
    obj = SomeClass()
    yield obj
    obj.cleanup()
```

Good code:

```python
import pytest

@pytest.fixture()
def my_fixture():
    obj = SomeClass()
    yield obj
    obj.cleanup()
```

## Rationale

* to avoid using the deprecated function


================================================
FILE: docs/rules/PT021.md
================================================
# PT021

`use yield instead of request.addfinalizer`

`pytest` offers two ways to perform cleanup in fixture code.  One is sequential
(`yield` statement), and the other is callback-based (`request.addfinalizer`).

`request.addfinalizer` is allowed when implementing [Factories as fixtures] pattern,
see examples below for more details.

## Examples

Bad code:

```python
import pytest

@pytest.fixture()
def my_fixture(request):
    resource = acquire_resource()
    request.addfinalizer(resource.release)
    return resource
```

Good code:
```python
import pytest

@pytest.fixture()
def my_fixture():
    resource = acquire_resource()
    yield resource
    resource.release()

# "Factories as fixtures" pattern
@pytest.fixture()
def my_factory(request):
    def create_resource(arg):
        resource = acquire_resource(arg)
        request.addfinalizer(resource.release)
        return resource
    return create_resource
```

## Rationale

* to make fixture code more linear and straightforward

[Factories as fixtures]: https://docs.pytest.org/en/stable/fixture.html#factories-as-fixtures


================================================
FILE: docs/rules/PT022.md
================================================
# PT022

`no teardown in fixture {name}, use return instead of yield`

`yield` in fixtures is only useful and semantically correct
when the fixture contains some teardown code.

## Examples

Bad code:

```python
import pytest

@pytest.fixture()
def my_fixture():
    resource = acquire_resource()
    yield resource
```

Good code:
```python
import pytest

@pytest.fixture()
def my_fixture_with_teardown():
    resource = acquire_resource()
    yield resource
    resource.release()

@pytest.fixture()
def my_fixture_without_teardown():
    resource = acquire_resource()
    return resource
```

## Rationale

* to make sure that all `yield` usages are semanticaly correct


================================================
FILE: docs/rules/PT023.md
================================================
# PT023

`use @pytest.mark.foo over @pytest.mark.foo()`

## Configuration

* `pytest-mark-no-parentheses`  
Boolean flag specifying whether `@pytest.mark.foo()` without parameters
should have parentheses.  
If the option is set to true (the default), `@pytest.mark.foo` is valid
and `@pytest.mark.foo()` is an error.  
If set to false, `@pytest.mark.foo()` is valid and `@pytest.mark.foo` is
an error.

## Examples

Bad code (assuming `pytest-mark-no-parentheses` set to true):

```python
import pytest

@pytest.mark.foo()
def test_something():
    ...
```

Good code:

```python
import pytest

@pytest.mark.foo
def test_something():
    ...
```

## Rationale

* to enforce consistency between all tests in a codebase


================================================
FILE: docs/rules/PT024.md
================================================
# PT024

`pytest.mark.asyncio is unnecessary for fixtures`

## Examples

Bad code:

```python
import pytest

@pytest.mark.asyncio()
@pytest.fixture()
async def my_fixture():
    return 0
```

Good code:

```python
import pytest

@pytest.fixture()
async def my_fixture():
    return 0
```

## Rationale

* the mark is useless on fixtures and therefore unnecessary


================================================
FILE: docs/rules/PT025.md
================================================
# PT025

`pytest.mark.usefixtures has no effect on fixtures`

## Examples

Bad code:

```python
import pytest

@pytest.fixture()
def a():
  pass

@pytest.mark.usefixtures('a')
@pytest.fixture()
def b():
  pass
```

Good code:

```python
import pytest

@pytest.fixture()
def a():
  pass

@pytest.fixture()
def b(a):
  pass
```

## Rationale

* according to the pytest [docs](https://docs.pytest.org/en/6.2.x/reference.html#pytest-mark-usefixtures)
  on `pytest.mark.usefixtures`:
   > Also note that this mark has no effect when applied to fixtures.
* pytest does not raise any error or warning when fixtures are decorated with `pytest.mark.usefixtures`,
  which can lead to incorrect results and broken tests

================================================
FILE: docs/rules/PT026.md
================================================
# PT023

`useless pytest.mark.usefixtures without parameters`

## Examples

Bad code:

```python
import pytest

@pytest.mark.usefixtures()
def test_something():
    ...

@pytest.mark.usefixtures
def test_something_else():
    ...
```

Good code:

```python
import pytest

@pytest.mark.usefixtures('a')
def test_something():
    ...
```

## Rationale

* such mark has no effect and is unnecessary


================================================
FILE: docs/rules/PT027.md
================================================
# PT027

`use pytest.raises() instead of unittest-style '{assertion}'`

## Examples

Bad code:

```python
import unittest

class TestFoo(unittest.TestCase):
    def test_foo(self):
        with self.assertRaises(ValueError):
            raise ValueError('foo')
```

Good code:

```python
import pytest
import unittest

class TestFoo(unittest.TestCase):
    def test_foo(self):
        with pytest.raises(ValueError):
            raise ValueError('foo')
```

## Rationale

* to enforce the assertion style recommended by pytest


================================================
FILE: docs/rules/PT028.md
================================================
# PT028

`test function {name} has default value for argument {arg}, remove it`

## Examples

Bad code:

```python
def test_foo(bar=42):
    pass
```

Good code:

```python
def test_foo(bar):
    pass
```

## Rationale

* even if the corresponding fixture is defined, current behavior of pytest is 
   to use the default value instead of injecting the fixture
* see original pytest issue: https://github.com/pytest-dev/pytest#12693


================================================
FILE: docs/rules/PT029.md
================================================
# PT029

`set the expected warning in pytest.warns()`

## Examples

Bad code:

```python
import pytest

def test_foo():
    with pytest.warns():
        do_something()
```

Good code:

```python
import pytest

def test_foo():
    with pytest.warns(SomeWarning):
        do_something()
```


## Rationale

* not passing the warning class will fail at runtime


================================================
FILE: docs/rules/PT030.md
================================================
# PT030

`pytest.warns({warning}) is too broad, set the match parameter or use a more specific warning`

## Configuration

* `pytest-warns-require-match-for`  
Comma-separated list of warning names that require a `match=` parameter
in a `pytest.warning()` call. By default the list contains the following
warnings:
  * `Warning`
  * `UserWarning`
  * `DeprecationWarning`

## Examples

Bad code:

```python
import pytest

def test_foo():
    with pytest.warns(UserWarning):
        ...

    # empty string is also an error
    with pytest.warns(UserWarning, match=''):
        ...
```

Good code:

```python
import pytest

def test_foo():
    with pytest.warns(UserWarning, match='expected message'):
        ...
```

## Rationale

* to help ensure that the `pytest.warns` clause is not too broad


================================================
FILE: docs/rules/PT031.md
================================================
# PT031

`pytest.warns() block should contain a single simple statement`

This forbids multiple statements and control flow structures within
`pytest.warns()` blocks.

## Examples

Bad code:

```python
import pytest

def test_foo():
    with pytest.warns(MyWarning):
        # if get_object() raises MyWarning, the test is incorrect
        obj = get_object()
        obj.do_something()

    with pytest.warns(MyWarning):
        if some_condition:
            do_something()
```

Good code:

```python
import pytest

def test_foo():
    obj = get_object()
    with pytest.warns(MyWarning):
        obj.do_something()
```

An empty `with`-statement (containing only a `pass` inside) is allowed
in order to allow testing of warnings raised by context manager enter/exit methods.
```python
import pytest

def test_my_context_manager():
    context_manager = get_context_manager()
    with pytest.warns(MyWarning):
        with context_manager:
            pass
```

## Rationale

* to help ensure that only the expected warning from code under test is
caught in a `pytest.warns` block (e.g. not an warning from
initialization/finalization code)


================================================
FILE: flake8_pytest_style/__init__.py
================================================


================================================
FILE: flake8_pytest_style/config.py
================================================
from enum import Enum
from typing import Any, List, NamedTuple, Type


def enum_choices(enum: Type[Enum]) -> List[Any]:
    return [member.value for member in enum]


class ParametrizeNamesType(Enum):
    CSV = "csv"
    TUPLE = "tuple"
    LIST = "list"


class ParametrizeValuesType(Enum):
    TUPLE = "tuple"
    LIST = "list"


class ParametrizeValuesRowType(Enum):
    TUPLE = "tuple"
    LIST = "list"


class Config(NamedTuple):
    fixture_parentheses: bool
    raises_require_match_for: List[str]
    warns_require_match_for: List[str]
    parametrize_names_type: ParametrizeNamesType
    parametrize_values_type: ParametrizeValuesType
    parametrize_values_row_type: ParametrizeValuesRowType
    mark_parentheses: bool


DEFAULT_CONFIG = Config(
    fixture_parentheses=False,
    raises_require_match_for=[
        "BaseException",
        "Exception",
        "ValueError",
        "IOError",
        "OSError",
        "EnvironmentError",
        "socket.error",
    ],
    warns_require_match_for=["Warning", "UserWarning", "DeprecationWarning"],
    parametrize_names_type=ParametrizeNamesType.TUPLE,
    parametrize_values_type=ParametrizeValuesType.LIST,
    parametrize_values_row_type=ParametrizeValuesRowType.TUPLE,
    mark_parentheses=False,
)


================================================
FILE: flake8_pytest_style/errors.py
================================================
from flake8_plugin_utils import Error


class IncorrectFixtureParenthesesStyle(Error):
    code = "PT001"
    message = "use @pytest.fixture{expected_parens} over @pytest.fixture{actual_parens}"


class FixturePositionalArgs(Error):
    code = "PT002"
    message = (
        "configuration for fixture '{name}' specified via positional args, use kwargs"
    )


class ExtraneousScopeFunction(Error):
    code = "PT003"
    message = "scope='function' is implied in @pytest.fixture()"


class MissingFixtureNameUnderscore(Error):
    code = "PT004"
    message = "fixture '{name}' does not return anything, add leading underscore"


class IncorrectFixtureNameUnderscore(Error):
    code = "PT005"
    message = "fixture '{name}' returns a value, remove leading underscore"


class ParametrizeNamesWrongType(Error):
    code = "PT006"
    message = "wrong name(s) type in @pytest.mark.parametrize, expected {expected_type}"


class ParametrizeValuesWrongType(Error):
    code = "PT007"
    message = "wrong values type in @pytest.mark.parametrize, expected {expected_type}"


class PatchWithLambda(Error):
    code = "PT008"
    message = "use return_value= instead of patching with lambda"


class UnittestAssertion(Error):
    code = "PT009"
    message = "use a regular assert instead of unittest-style '{assertion}'"


class RaisesWithoutException(Error):
    code = "PT010"
    message = "set the expected exception in pytest.raises()"


class RaisesTooBroad(Error):
    code = "PT011"
    message = (
        "pytest.raises({exception}) is too broad,"
        " set the match parameter or use a more specific exception"
    )


class RaisesWithMultipleStatements(Error):
    code = "PT012"
    message = "pytest.raises() block should contain a single simple statement"


class IncorrectPytestImport(Error):
    code = "PT013"
    message = "found incorrect import of pytest, use simple 'import pytest' instead"


class DuplicateParametrizeTestCases(Error):
    code = "PT014"
    message = "found duplicate test cases {indexes} in @pytest.mark.parametrize"


class AssertAlwaysFalse(Error):
    code = "PT015"
    message = "assertion always fails, replace with pytest.fail()"


class FailWithoutMessage(Error):
    code = "PT016"
    message = "no message passed to pytest.fail()"


class AssertInExcept(Error):
    code = "PT017"
    message = (
        "found assertion on exception {name} in except block,"
        " use pytest.raises() instead"
    )


class CompositeAssertion(Error):
    code = "PT018"
    message = "assertion should be broken down into multiple parts"


class FixtureParamWithoutValue(Error):
    code = "PT019"
    message = (
        "fixture {name} without value is injected as parameter,"
        " use @pytest.mark.usefixtures instead"
    )


class DeprecatedYieldFixture(Error):
    code = "PT020"
    message = "@pytest.yield_fixture is deprecated, use @pytest.fixture"


class FixtureFinalizerCallback(Error):
    code = "PT021"
    message = "use yield instead of request.addfinalizer"


class UselessYieldFixture(Error):
    code = "PT022"
    message = "no teardown in fixture {name}, use return instead of yield"


class IncorrectMarkParenthesesStyle(Error):
    code = "PT023"
    message = (
        "use @pytest.mark.{mark_name}{expected_parens}"
        " over @pytest.mark.{mark_name}{actual_parens}"
    )


class UnnecessaryAsyncioMarkOnFixture(Error):
    code = "PT024"
    message = "pytest.mark.asyncio is unnecessary for fixtures"


class ErroneousUseFixturesOnFixture(Error):
    code = "PT025"
    message = "pytest.mark.usefixtures has no effect on fixtures"


class UseFixturesWithoutParameters(Error):
    code = "PT026"
    message = "useless pytest.mark.usefixtures without parameters"


class UnittestRaisesAssertion(Error):
    code = "PT027"
    message = "use pytest.raises() instead of unittest-style '{assertion}'"


# This should be named `TestFunctionArgumentWithDefault`,
# but this way it is easier to avoid confusion with the `Test` prefix in pytest.
class TFunctionArgumentWithDefault(Error):
    code = "PT028"
    message = "test function {name} has default value for argument {arg}, remove it"


class WarnsWithoutException(Error):
    code = "PT029"
    message = "set the expected warning in pytest.warns()"


class WarnsTooBroad(Error):
    code = "PT030"
    message = (
        "pytest.warns({warning}) is too broad,"
        " set the match parameter or use a more specific warning"
    )


class WarnsWithMultipleStatements(Error):
    code = "PT031"
    message = "pytest.warns() block should contain a single simple statement"


================================================
FILE: flake8_pytest_style/plugin.py
================================================
import argparse
from typing import List

from flake8.options.manager import OptionManager
from flake8_plugin_utils import Plugin

from .config import (
    DEFAULT_CONFIG,
    Config,
    ParametrizeNamesType,
    ParametrizeValuesRowType,
    ParametrizeValuesType,
    enum_choices,
)
from .visitors import (
    AssertionVisitor,
    FailVisitor,
    FixturesVisitor,
    ImportsVisitor,
    MarksVisitor,
    ParametrizeVisitor,
    PatchVisitor,
    RaisesVisitor,
    TFunctionsVisitor,
    TryExceptVisitor,
    UnittestAssertionVisitor,
    WarnsVisitor,
)

__version__ = "2.2.0"


class PytestStylePlugin(Plugin[Config]):
    name = "flake8-pytest-style"
    version = __version__
    visitors = [
        AssertionVisitor,
        FailVisitor,
        FixturesVisitor,
        ImportsVisitor,
        MarksVisitor,
        PatchVisitor,
        ParametrizeVisitor,
        RaisesVisitor,
        TFunctionsVisitor,
        TryExceptVisitor,
        UnittestAssertionVisitor,
        WarnsVisitor,
    ]

    @classmethod
    def add_options(cls, option_manager: OptionManager) -> None:
        option_manager.add_option(
            "--pytest-fixture-no-parentheses",
            action="store_true",
            parse_from_config=True,
            default=not DEFAULT_CONFIG.fixture_parentheses,
            help="Omit parentheses for @pytest.fixture decorators"
            " without parameters. (Default: %(default)s)",
        )
        option_manager.add_option(
            "--pytest-raises-require-match-for",
            comma_separated_list=True,
            parse_from_config=True,
            default=DEFAULT_CONFIG.raises_require_match_for,
            help="List of exceptions for which flake8-pytest-style requires"
            " a match= argument in pytest.raises(). (Default: %(default)s)",
        )
        option_manager.add_option(
            "--pytest-parametrize-names-type",
            choices=enum_choices(ParametrizeNamesType),
            parse_from_config=True,
            default=DEFAULT_CONFIG.parametrize_names_type.value,
            help="Preferred type for multiple parameter names in"
            " @pytest.mark.parametrize. (Default: %(default)s)",
        )
        option_manager.add_option(
            "--pytest-parametrize-values-type",
            choices=enum_choices(ParametrizeValuesType),
            parse_from_config=True,
            default=DEFAULT_CONFIG.parametrize_values_type.value,
            help="Preferred type for values in @pytest.mark.parametrize."
            " (Default: %(default)s)",
        )
        option_manager.add_option(
            "--pytest-parametrize-values-row-type",
            choices=enum_choices(ParametrizeValuesRowType),
            parse_from_config=True,
            default=DEFAULT_CONFIG.parametrize_values_row_type.value,
            help="Preferred type for each row in @pytest.mark.parametrize"
            " in case of multiple parameters. (Default: %(default)s)",
        )
        option_manager.add_option(
            "--pytest-mark-no-parentheses",
            action="store_true",
            parse_from_config=True,
            default=not DEFAULT_CONFIG.mark_parentheses,
            help="Omit parentheses for @pytest.mark.foo decorators"
            " without parameters. (Default: %(default)s)",
        )
        option_manager.add_option(
            "--pytest-warns-require-match-for",
            comma_separated_list=True,
            parse_from_config=True,
            default=DEFAULT_CONFIG.warns_require_match_for,
            help="List of warnings for which flake8-pytest-style requires"
            " a match= argument in pytest.warns(). (Default: %(default)s)",
        )

    @classmethod
    def parse_options_to_config(  # pylint: disable=unused-argument
        cls, option_manager: OptionManager, options: argparse.Namespace, args: List[str]
    ) -> Config:
        return Config(
            fixture_parentheses=not options.pytest_fixture_no_parentheses,
            raises_require_match_for=options.pytest_raises_require_match_for,
            parametrize_names_type=ParametrizeNamesType(
                options.pytest_parametrize_names_type
            ),
            parametrize_values_type=ParametrizeValuesType(
                options.pytest_parametrize_values_type
            ),
            parametrize_values_row_type=ParametrizeValuesRowType(
                options.pytest_parametrize_values_row_type
            ),
            mark_parentheses=not options.pytest_mark_no_parentheses,
            warns_require_match_for=options.pytest_warns_require_match_for,
        )


================================================
FILE: flake8_pytest_style/py.typed
================================================


================================================
FILE: flake8_pytest_style/utils.py
================================================
import ast
from collections import deque
from typing import Dict, Iterator, List, NamedTuple, Optional, Tuple, Union

AnyFunctionDef = Union[ast.AsyncFunctionDef, ast.FunctionDef]
AnyDecoratorTarget = Union[ast.ClassDef, AnyFunctionDef]


def get_qualname(node: ast.AST) -> Optional[str]:
    """
    If node represents a chain of attribute accesses, return is qualified name.
    """
    parts = []
    while True:
        if isinstance(node, ast.Name):
            parts.append(node.id)
            break
        if isinstance(node, ast.Attribute):
            parts.append(node.attr)
            node = node.value
        else:
            return None
    return ".".join(reversed(parts))


class SimpleCallArgs(NamedTuple):
    args: Tuple[ast.AST, ...]
    kwargs: Dict[str, ast.AST]

    def get_argument(
        self, name: str, position: Optional[int] = None
    ) -> Optional[ast.AST]:
        """Get argument by name in kwargs or position in args."""
        kwarg = self.kwargs.get(name)
        if kwarg is not None:
            return kwarg
        if position is not None and len(self.args) > position:
            return self.args[position]
        return None


def get_simple_call_args(node: ast.Call) -> SimpleCallArgs:
    """
    Get call arguments which are specified explicitly (positional and keyword).
    """

    # list of leading non-starred args
    args = []
    for arg in node.args:
        if isinstance(arg, ast.Starred):
            break
        args.append(arg)

    # dict of keyword args
    keywords: Dict[str, ast.AST] = {}
    for keyword in node.keywords:
        if keyword.arg is not None:
            keywords[keyword.arg] = keyword.value

    return SimpleCallArgs(tuple(args), keywords)


def is_parametrize_call(node: ast.Call) -> bool:
    """Checks if given call is to `pytest.mark.parametrize`."""
    return get_qualname(node.func) == "pytest.mark.parametrize"


def is_raises_call(node: ast.Call) -> bool:
    """Checks if given call is to `pytest.raises`."""
    return get_qualname(node.func) == "pytest.raises"


def is_warns_call(node: ast.Call) -> bool:
    """Checks if given call is to `pytest.warns`."""
    return get_qualname(node.func) == "pytest.warns"


def is_fail_call(node: ast.Call) -> bool:
    """Checks if given call is to `pytest.fail`."""
    return get_qualname(node.func) == "pytest.fail"


def is_raises_with(node: ast.With) -> bool:
    """Checks that a given `with` statement has a `pytest.raises` context."""
    for item in node.items:
        if isinstance(item.context_expr, ast.Call) and is_raises_call(
            item.context_expr
        ):
            return True
    return False


def is_warns_with(node: ast.With) -> bool:
    """Checks that a given `with` statement has a `pytest.warns` context."""
    for item in node.items:
        if isinstance(item.context_expr, ast.Call) and is_warns_call(item.context_expr):
            return True
    return False


class ParametrizeArgs(NamedTuple):
    names: ast.AST
    values: Optional[ast.AST]
    ids: Optional[ast.AST]


def extract_parametrize_call_args(node: ast.Call) -> Optional[ParametrizeArgs]:
    """Extracts argnames, argvalues and ids from a parametrize call."""
    args = get_simple_call_args(node)

    names_arg = args.get_argument("argnames", 0)
    if names_arg is None:
        return None

    values_arg = args.get_argument("argvalues", 1)
    ids_arg = args.get_argument("ids")
    return ParametrizeArgs(names_arg, values_arg, ids_arg)


def _is_pytest_fixture(node: ast.AST) -> bool:
    """Checks if node is a `pytest.fixture` attribute access."""
    return get_qualname(node) == "pytest.fixture"


def is_pytest_yield_fixture(node: ast.AST) -> bool:
    """Checks if node is a `pytest.yield_fixture` attribute access."""
    return get_qualname(node) == "pytest.yield_fixture"


def _is_any_pytest_fixture(node: ast.AST) -> bool:
    """Checks if node is a `pytest.fixture` or `pytest.yield_fixture`."""
    return _is_pytest_fixture(node) or is_pytest_yield_fixture(node)


def get_fixture_decorator(node: AnyFunctionDef) -> Union[ast.Call, ast.Attribute, None]:
    """
    Returns a @pytest.fixture decorator applied to given function definition, if any.

    Return value is either:
    * ast.Call, if decorator is written as @pytest.fixture()
    * ast.Attribute, if decorator is written as @pytest.fixture
    * None, if decorator not found
    """
    for decorator in node.decorator_list:
        if (
            isinstance(decorator, ast.Call)
            and isinstance(decorator.func, ast.Attribute)
            and _is_any_pytest_fixture(decorator.func)
        ):
            return decorator
        if isinstance(decorator, ast.Attribute) and _is_any_pytest_fixture(decorator):
            return decorator

    return None


def _is_mark(node: ast.AST) -> bool:
    """Checks if node is a `pytest.mark.foo` attribute access."""
    qualname = get_qualname(node)
    if qualname is None:
        return False
    return qualname.startswith("pytest.mark.")


def get_mark_decorators(
    node: AnyDecoratorTarget,
) -> List[Union[ast.Call, ast.Attribute]]:
    """
    Returns all @pytest.mark.foo decorators applied to given function definition.

    Return value is a list of:
    * ast.Call, if decorator is written as @pytest.mark.foo()
    * ast.Attribute, if decorator is written as @pytest.mark.foo
    """
    result: List[Union[ast.Call, ast.Attribute]] = []
    for decorator in node.decorator_list:
        if (
            isinstance(decorator, ast.Call)
            and isinstance(decorator.func, ast.Attribute)
            and _is_mark(decorator.func)
        ):
            result.append(decorator)
        if isinstance(decorator, ast.Attribute) and _is_mark(decorator):
            result.append(decorator)
    return result


def get_mark_name(node: ast.AST) -> str:
    """
    Returns the name of the mark in a `pytest.mark.foo` attribute access
    or in a `pytest.mark.foo()` call.

    If the given node is not suitable as described above, a ValueError is raised.
    """
    mark_prefix = "pytest.mark."
    if isinstance(node, ast.Call):
        node = node.func
    qualname = get_qualname(node)
    if qualname is None or not qualname.startswith(mark_prefix):
        raise ValueError("Given node is not a pytest mark")
    return qualname[len(mark_prefix) :]  # noqa: E203


def is_none(node: ast.AST) -> bool:
    """
    Checks if the node is a constant representing the value `None`.

    Drop-in replacement for `flake8_plugin_utils.utils.is_none` without using
    removed `ast.NameConstant` class.
    """

    return isinstance(node, ast.Constant) and node.value is None


def is_empty_string(node: ast.AST) -> bool:
    """
    Checks if the node is a constant empty string.
    """

    # empty string literal
    if isinstance(node, ast.Constant) and node.value == "":
        return True

    # empty f-string
    if isinstance(node, ast.JoinedStr) and not node.values:
        return True

    return False


def _is_empty_iterable(  # pylint:disable=too-many-return-statements
    node: ast.AST,
) -> bool:
    """
    Checks if the node is a constant empty iterable.
    """

    if is_empty_string(node):
        return True

    # empty list or tuple literal
    if isinstance(node, (ast.List, ast.Tuple)) and not node.elts:
        return True

    # empty dict literal
    if isinstance(node, ast.Dict) and not node.keys:
        return True

    if isinstance(node, ast.Call) and get_qualname(node.func) in (
        "list",
        "set",
        "tuple",
        "dict",
        "frozenset",
    ):
        if not node.args and not node.keywords:  # no args
            return True
        if (
            len(node.args) == 1
            and not node.keywords
            and _is_empty_iterable(node.args[0])
        ):  # single arg, empty iterable
            return True

    return False


def is_falsy_constant(node: ast.AST) -> bool:
    """
    Checks if the node is a constant with a falsy value.
    """

    # constant node: None, False, zero, empty string (not f-string)
    if isinstance(node, ast.Constant) and not node.value:
        return True

    return _is_empty_iterable(node)


def is_test_function(node: AnyFunctionDef) -> bool:
    """Checks if the given function is a test function."""
    return node.name.startswith("test_")


def get_all_argument_names(node: ast.arguments) -> List[str]:
    """Returns a list of all argument names from the given node."""
    pos_only_args = getattr(node, "posonlyargs", [])
    result = [arg.arg for arg in pos_only_args + node.args]
    if node.vararg:
        result.append(node.vararg.arg)
    result.extend([arg.arg for arg in node.kwonlyargs])
    if node.kwarg:
        result.append(node.kwarg.arg)
    return result


def walk_without_nested_functions(root: ast.AST) -> Iterator[ast.AST]:
    """Similar to `ast.walk`, but does not descend into nested functions."""

    todo = deque([root])
    while todo:
        node = todo.popleft()
        if node is root or not isinstance(
            node, (ast.FunctionDef, ast.AsyncFunctionDef)
        ):
            todo.extend(ast.iter_child_nodes(node))
        yield node


def is_abstract_method(node: AnyFunctionDef) -> bool:
    """
    Returns true if the node is decorated with an abstract method decorator,
    otherwise false.
    """
    for decorator in node.decorator_list:
        qualname = get_qualname(decorator)
        if qualname in ("abstractmethod", "abc.abstractmethod"):
            return True
    return False


def is_nontrivial_with_statement(node: ast.AST) -> bool:
    """
    Returns true if the given node is a `with` (or `async with`) statement
    containing any code inside (anything which is not a single `pass` statement).
    """
    if not isinstance(node, (ast.With, ast.AsyncWith)):
        return False
    body = node.body
    return len(node.body) != 1 or not isinstance(body[0], ast.Pass)


================================================
FILE: flake8_pytest_style/visitors/__init__.py
================================================
from .assertion import AssertionVisitor, UnittestAssertionVisitor
from .fail import FailVisitor
from .fixtures import FixturesVisitor
from .imports import ImportsVisitor
from .marks import MarksVisitor
from .parametrize import ParametrizeVisitor
from .patch import PatchVisitor
from .raises import RaisesVisitor
from .t_functions import TFunctionsVisitor
from .try_except import TryExceptVisitor
from .warns import WarnsVisitor

__all__ = (
    "AssertionVisitor",
    "FailVisitor",
    "FixturesVisitor",
    "ImportsVisitor",
    "MarksVisitor",
    "ParametrizeVisitor",
    "PatchVisitor",
    "RaisesVisitor",
    "TFunctionsVisitor",
    "TryExceptVisitor",
    "UnittestAssertionVisitor",
    "WarnsVisitor",
)


================================================
FILE: flake8_pytest_style/visitors/assertion.py
================================================
import ast

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import (
    CompositeAssertion,
    UnittestAssertion,
    UnittestRaisesAssertion,
)

_UNITTEST_ASSERT_NAMES = (
    # taken from dir(unittest.TestCase) under Python 3.11
    "assertAlmostEqual",
    "assertAlmostEquals",
    "assertCountEqual",
    "assertDictContainsSubset",
    "assertDictEqual",
    "assertEqual",
    "assertEquals",
    "assertFalse",
    "assertGreater",
    "assertGreaterEqual",
    "assertIn",
    "assertIs",
    "assertIsInstance",
    "assertIsNone",
    "assertIsNot",
    "assertIsNotNone",
    "assertLess",
    "assertLessEqual",
    "assertListEqual",
    "assertLogs",
    "assertMultiLineEqual",
    "assertNoLogs",
    "assertNotAlmostEqual",
    "assertNotAlmostEquals",
    "assertNotEqual",
    "assertNotEquals",
    "assertNotIn",
    "assertNotIsInstance",
    "assertNotRegex",
    "assertNotRegexpMatches",
    "assertRegex",
    "assertRegexpMatches",
    "assertSequenceEqual",
    "assertSetEqual",
    "assertTrue",
    "assertTupleEqual",
    "assertWarns",
    "assertWarnsRegex",
    "assert_",
    "failIf",
    "failIfAlmostEqual",
    "failIfEqual",
    "failUnless",
    "failUnlessAlmostEqual",
    "failUnlessEqual",
    # below is from Django's SimpleTestCase, leaked here when porting flake8-pytest
    # TODO(m_burst) disallow Django's SimpleTestCase/TransactionTestCase assertions
    #  where feasible (#220)
    "assertNotContains",
)

_UNITTEST_ASSERT_RAISES_NAMES = (
    # taken from dir(unittest.TestCase) under Python 3.11
    "assertRaises",
    "assertRaisesRegex",
    "assertRaisesRegexp",
    "failUnlessRaises",
    # below is from Django's SimpleTestCase, leaked here when porting flake8-pytest
    "assertRaisesMessage",
)


class UnittestAssertionVisitor(Visitor[Config]):
    def visit_Call(self, node: ast.Call) -> None:
        if isinstance(node.func, ast.Attribute):
            if node.func.attr in _UNITTEST_ASSERT_NAMES:
                self.error_from_node(UnittestAssertion, node, assertion=node.func.attr)
            elif node.func.attr in _UNITTEST_ASSERT_RAISES_NAMES:
                self.error_from_node(
                    UnittestRaisesAssertion, node, assertion=node.func.attr
                )


class AssertionVisitor(Visitor[Config]):
    def _is_composite_condition(self, expr: ast.AST) -> bool:
        # e.g. `a and b`
        if isinstance(expr, ast.BoolOp) and isinstance(expr.op, ast.And):
            return True

        # e.g. `not (a or b)`
        if (
            isinstance(expr, ast.UnaryOp)
            and isinstance(expr.op, ast.Not)
            and isinstance(expr.operand, ast.BoolOp)
            and isinstance(expr.operand.op, ast.Or)
        ):
            return True

        return False

    def visit_Assert(self, node: ast.Assert) -> None:
        if self._is_composite_condition(node.test):
            self.error_from_node(CompositeAssertion, node)


================================================
FILE: flake8_pytest_style/visitors/fail.py
================================================
import ast

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import AssertAlwaysFalse, FailWithoutMessage
from flake8_pytest_style.utils import (
    get_simple_call_args,
    is_empty_string,
    is_fail_call,
    is_falsy_constant,
)


class FailVisitor(Visitor[Config]):
    def _check_fail_call(self, node: ast.Call) -> None:
        """Checks for PT016."""
        args = get_simple_call_args(node)
        # Since pytest 7.0 the argument is named 'reason', and 'msg' is deprecated but
        # supported (at the time of writing this code).  We check 'reason', then first
        # positional argument, and then 'msg'.  The edge cases like
        # `pytest.fail('foo', msg='bar')` and `pytest.fail(reason='foo', msg='bar')`
        # are deliberately ignored.
        message_argument = args.get_argument("reason", 0) or args.get_argument("msg")
        if not message_argument or is_empty_string(message_argument):
            self.error_from_node(FailWithoutMessage, node)

    def visit_Assert(self, node: ast.Assert) -> None:
        """Checks for PT015."""
        if is_falsy_constant(node.test):
            self.error_from_node(AssertAlwaysFalse, node)

    def visit_Call(self, node: ast.Call) -> None:
        if is_fail_call(node):
            self._check_fail_call(node)


================================================
FILE: flake8_pytest_style/visitors/fixtures.py
================================================
import ast
from typing import Set, Type, Union

from flake8_plugin_utils import Error, Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import (
    DeprecatedYieldFixture,
    ErroneousUseFixturesOnFixture,
    ExtraneousScopeFunction,
    FixtureFinalizerCallback,
    FixturePositionalArgs,
    IncorrectFixtureNameUnderscore,
    IncorrectFixtureParenthesesStyle,
    MissingFixtureNameUnderscore,
    UnnecessaryAsyncioMarkOnFixture,
    UselessYieldFixture,
)
from flake8_pytest_style.utils import (
    AnyFunctionDef,
    get_all_argument_names,
    get_fixture_decorator,
    get_mark_decorators,
    get_mark_name,
    get_qualname,
    is_abstract_method,
    is_pytest_yield_fixture,
    walk_without_nested_functions,
)


class FixturesVisitor(Visitor[Config]):
    def _check_fixture_decorator_name(
        self, fixture_decorator: Union[ast.Call, ast.Attribute]
    ) -> None:
        """Checks for PT020."""
        if isinstance(fixture_decorator, ast.Call):
            is_yield = is_pytest_yield_fixture(fixture_decorator.func)
        else:
            is_yield = is_pytest_yield_fixture(fixture_decorator)
        if is_yield:
            self.error_from_node(DeprecatedYieldFixture, fixture_decorator)

    def _check_fixture_decorator(
        self,
        fixture_decorator: Union[ast.Call, ast.Attribute],
        fixture_func: AnyFunctionDef,
    ) -> None:
        """Checks for PT001, PT002, PT003."""
        if not isinstance(fixture_decorator, ast.Call):
            if self.config.fixture_parentheses:
                self.error_from_node(
                    IncorrectFixtureParenthesesStyle,
                    fixture_decorator,
                    expected_parens="()",
                    actual_parens="",
                )
            return

        if (
            not self.config.fixture_parentheses
            and not fixture_decorator.args
            and not fixture_decorator.keywords
        ):
            self.error_from_node(
                IncorrectFixtureParenthesesStyle,
                fixture_decorator,
                expected_parens="",
                actual_parens="()",
            )

        if fixture_decorator.args:
            self.error_from_node(
                FixturePositionalArgs, fixture_decorator, name=fixture_func.name
            )

        for keyword in fixture_decorator.keywords:
            if (
                keyword.arg == "scope"
                and isinstance(keyword.value, ast.Constant)
                and keyword.value.value == "function"
            ):
                self.error_from_node(
                    ExtraneousScopeFunction, fixture_decorator, name=fixture_func.name
                )

    def _check_fixture_returns(self, node: AnyFunctionDef) -> None:
        """Checks for PT004, PT005, PT022."""
        # skip these checks for abstract fixtures
        if is_abstract_method(node):
            return

        has_return_with_value = False
        has_yield_from = False
        yield_statements = []
        for child in walk_without_nested_functions(node):
            if isinstance(child, ast.Yield):
                yield_statements.append(child)
            if isinstance(child, (ast.Return, ast.Yield)) and child.value is not None:
                has_return_with_value = True
            if isinstance(child, ast.YieldFrom):
                has_yield_from = True

        if has_return_with_value and node.name.startswith("_"):
            self.error_from_node(IncorrectFixtureNameUnderscore, node, name=node.name)
        elif (
            not has_return_with_value
            and not has_yield_from
            and not node.name.startswith("_")
        ):
            # we shouldn't fire PT004 if we found a `yield from` because
            # there is no adequate way to determine whether a value is actually yielded
            self.error_from_node(MissingFixtureNameUnderscore, node, name=node.name)

        last_statement_is_yield = isinstance(node.body[-1], ast.Expr) and isinstance(
            node.body[-1].value, ast.Yield
        )
        if last_statement_is_yield and len(yield_statements) == 1:
            self.error_from_node(UselessYieldFixture, node, name=node.name)

    def _check_fixture_addfinalizer(self, node: AnyFunctionDef) -> None:
        """Checks for PT021."""
        if "request" not in get_all_argument_names(node.args):
            return

        for child in walk_without_nested_functions(node):  # pragma: no branch
            if (
                isinstance(child, ast.Call)
                and get_qualname(child.func) == "request.addfinalizer"
            ):
                self.error_from_node(FixtureFinalizerCallback, child)
                return

    def _check_fixture_marks(self, node: AnyFunctionDef) -> None:
        """Checks for PT024, PT025."""
        reported_errors: Set[Type[Error]] = set()
        marks = get_mark_decorators(node)
        for mark in marks:
            mark_name = get_mark_name(mark)
            if (
                mark_name == "asyncio"
                and UnnecessaryAsyncioMarkOnFixture not in reported_errors
            ):
                self.error_from_node(UnnecessaryAsyncioMarkOnFixture, mark)
                reported_errors.add(UnnecessaryAsyncioMarkOnFixture)
            if (
                mark_name == "usefixtures"
                and ErroneousUseFixturesOnFixture not in reported_errors
            ):
                self.error_from_node(ErroneousUseFixturesOnFixture, mark)
                reported_errors.add(ErroneousUseFixturesOnFixture)

    def visit_FunctionDef(self, node: AnyFunctionDef) -> None:
        fixture_decorator = get_fixture_decorator(node)
        if fixture_decorator:
            self._check_fixture_decorator_name(fixture_decorator)
            self._check_fixture_decorator(fixture_decorator, node)
            self._check_fixture_returns(node)
            self._check_fixture_addfinalizer(node)
            self._check_fixture_marks(node)

        self.generic_visit(node)

    visit_AsyncFunctionDef = visit_FunctionDef  # noqa: N815


================================================
FILE: flake8_pytest_style/visitors/imports.py
================================================
import ast

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import IncorrectPytestImport


def _is_pytest_or_subpackage(imported_name: str) -> bool:
    return imported_name == "pytest" or imported_name.startswith("pytest.")


class ImportsVisitor(Visitor[Config]):
    def visit_Import(self, node: ast.Import) -> None:
        for name in node.names:
            if (
                _is_pytest_or_subpackage(name.name)
                and name.asname
                and name.asname != name.name
            ):
                self.error_from_node(IncorrectPytestImport, node)

    def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
        if node.level != 0 or node.module is None:  # relative import
            return
        if _is_pytest_or_subpackage(node.module):
            self.error_from_node(IncorrectPytestImport, node)


================================================
FILE: flake8_pytest_style/visitors/marks.py
================================================
import ast
from typing import Union

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import (
    IncorrectMarkParenthesesStyle,
    UseFixturesWithoutParameters,
)
from flake8_pytest_style.utils import (
    AnyDecoratorTarget,
    get_mark_decorators,
    get_mark_name,
)


class MarksVisitor(Visitor[Config]):
    def _check_mark_parentheses(
        self, mark_decorator: Union[ast.Call, ast.Attribute]
    ) -> None:
        """Checks for PT023."""

        if not isinstance(mark_decorator, ast.Call):
            if self.config.mark_parentheses:
                self.error_from_node(
                    IncorrectMarkParenthesesStyle,
                    mark_decorator,
                    mark_name=get_mark_name(mark_decorator),
                    expected_parens="()",
                    actual_parens="",
                )
            return

        if (
            not self.config.mark_parentheses
            and not mark_decorator.args
            and not mark_decorator.keywords
        ):
            self.error_from_node(
                IncorrectMarkParenthesesStyle,
                mark_decorator,
                mark_name=get_mark_name(mark_decorator.func),
                expected_parens="",
                actual_parens="()",
            )

    def _check_useless_usefixtures(
        self, mark_decorator: Union[ast.Call, ast.Attribute]
    ) -> None:
        """Checks for PT026."""

        if get_mark_name(mark_decorator) != "usefixtures":
            return

        has_parameters = isinstance(mark_decorator, ast.Call) and bool(
            mark_decorator.args or mark_decorator.keywords
        )
        if not has_parameters:
            self.error_from_node(UseFixturesWithoutParameters, mark_decorator)

    def visit_FunctionDef(self, node: AnyDecoratorTarget) -> None:
        mark_decorators = get_mark_decorators(node)
        for mark_decorator in mark_decorators:
            self._check_mark_parentheses(mark_decorator)
            self._check_useless_usefixtures(mark_decorator)

    visit_AsyncFunctionDef = visit_FunctionDef  # noqa: N815

    def visit_ClassDef(self, node: ast.ClassDef) -> None:
        self.visit_FunctionDef(node)

        # recurse into classes
        self.generic_visit(node)


================================================
FILE: flake8_pytest_style/visitors/parametrize.py
================================================
import ast
import itertools
from typing import Optional

from flake8_plugin_utils import Visitor, check_equivalent_nodes

from ..config import (
    Config,
    ParametrizeNamesType,
    ParametrizeValuesRowType,
    ParametrizeValuesType,
)
from ..errors import (
    DuplicateParametrizeTestCases,
    ParametrizeNamesWrongType,
    ParametrizeValuesWrongType,
)
from ..utils import extract_parametrize_call_args, is_parametrize_call


class ParametrizeVisitor(Visitor[Config]):
    def _check_parametrize_names(
        self, node: ast.Call, names: ast.AST
    ) -> Optional[bool]:
        """
        Handles names in parametrize, checks for PT006.

        Returns a flag indicating whether parametrize has multiple names,
        or None if we can't tell.
        """

        multiple_names: Optional[bool] = None
        found_type: Optional[ParametrizeNamesType] = None
        if isinstance(names, ast.Constant) and isinstance(names.value, str):
            if "," in names.value:
                found_type = ParametrizeNamesType.CSV
                multiple_names = True
            else:
                multiple_names = False
        elif isinstance(names, (ast.List, ast.Tuple)):
            multiple_names = len(names.elts) > 1
            if not multiple_names:
                self.error_from_node(
                    ParametrizeNamesWrongType, node, expected_type="string"
                )
            elif isinstance(names, ast.Tuple):
                found_type = ParametrizeNamesType.TUPLE
            else:
                found_type = ParametrizeNamesType.LIST
        if multiple_names and found_type != self.config.parametrize_names_type:
            self.error_from_node(
                ParametrizeNamesWrongType,
                node,
                expected_type=self.config.parametrize_names_type.value,
            )
        return multiple_names

    def _get_expected_values_type_str(self, multiple_names: Optional[bool]) -> str:
        if multiple_names:
            return (
                f"{self.config.parametrize_values_type.value}"
                f" of {self.config.parametrize_values_row_type.value}s"
            )
        return self.config.parametrize_values_type.value

    def _check_parametrize_values(
        self, node: ast.Call, values: Optional[ast.AST], multiple_names: Optional[bool]
    ) -> None:
        """Checks for PT007."""
        expected_type_str = self._get_expected_values_type_str(multiple_names)

        if isinstance(values, ast.List):
            top_level_type = ParametrizeValuesType.LIST
        elif isinstance(values, ast.Tuple):
            top_level_type = ParametrizeValuesType.TUPLE
        else:
            return

        if top_level_type != self.config.parametrize_values_type:
            self.error_from_node(
                ParametrizeValuesWrongType, node, expected_type=expected_type_str
            )
            return

        if multiple_names:
            for element in values.elts:
                found_row_type: Optional[ParametrizeValuesRowType] = None
                if isinstance(element, ast.List):
                    found_row_type = ParametrizeValuesRowType.LIST
                elif isinstance(element, ast.Tuple):
                    found_row_type = ParametrizeValuesRowType.TUPLE
                if (
                    found_row_type
                    and found_row_type != self.config.parametrize_values_row_type
                ):
                    self.error_from_node(
                        ParametrizeValuesWrongType,
                        node,
                        expected_type=expected_type_str,
                    )
                    break

    def _check_parametrize_duplicates(
        self, node: ast.AST, values: Optional[ast.AST]
    ) -> None:
        """Checks for PT014."""
        if not isinstance(values, (ast.List, ast.Tuple, ast.Set)):
            return

        for (i, element1), (j, element2) in itertools.combinations(
            enumerate(values.elts, start=1), 2
        ):
            if check_equivalent_nodes(element1, element2):
                self.error_from_node(
                    DuplicateParametrizeTestCases, node, indexes=(i, j)
                )

    def _check_parametrize_call(self, node: ast.Call) -> None:
        """Checks for all violations regarding `pytest.mark.parametrize` calls."""
        args = extract_parametrize_call_args(node)
        if not args:
            return

        multiple_names = self._check_parametrize_names(node, args.names)

        self._check_parametrize_values(node, args.values, multiple_names)

        self._check_parametrize_duplicates(node, args.values)

    def visit_Call(self, node: ast.Call) -> None:
        if is_parametrize_call(node):
            self._check_parametrize_call(node)


================================================
FILE: flake8_pytest_style/visitors/patch.py
================================================
import ast

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import PatchWithLambda
from flake8_pytest_style.utils import (
    get_all_argument_names,
    get_qualname,
    get_simple_call_args,
)

_PATCH_NAMESPACES = (
    "mocker",
    "class_mocker",
    "module_mocker",
    "package_mocker",
    "session_mocker",
    "mock",
    "unittest.mock",
)
_PATCH_NAMES = ("patch",) + tuple(
    f"{namespace}.patch" for namespace in _PATCH_NAMESPACES
)
_PATCH_OBJECT_NAMES = tuple(f"{name}.object" for name in _PATCH_NAMES)


class PatchVisitor(Visitor[Config]):
    def _check_patch_call(self, node: ast.Call, new_arg_number: int) -> None:
        """
        Checks for PT008.

        :param node: patch call node
        :param new_arg_number: number of `new` positional argument of patch func
        """
        args = get_simple_call_args(node)
        if args.get_argument("return_value") is not None:
            return

        new_arg = args.get_argument("new", new_arg_number)
        if not isinstance(new_arg, ast.Lambda):
            return

        lambda_argnames = set(get_all_argument_names(new_arg.args))

        for child_node in ast.walk(new_arg.body):
            if isinstance(child_node, ast.Name) and child_node.id in lambda_argnames:
                break
        else:
            self.error_from_node(PatchWithLambda, node)

    def visit_Call(self, node: ast.Call) -> None:
        if get_qualname(node.func) in _PATCH_NAMES:
            # attributes are (target, new, ...)
            self._check_patch_call(node, 1)

        if get_qualname(node.func) in _PATCH_OBJECT_NAMES:
            # attributes are (target, attribute, new, ...)
            self._check_patch_call(node, 2)


================================================
FILE: flake8_pytest_style/visitors/raises.py
================================================
import ast

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import (
    RaisesTooBroad,
    RaisesWithMultipleStatements,
    RaisesWithoutException,
)
from flake8_pytest_style.utils import (
    get_qualname,
    get_simple_call_args,
    is_empty_string,
    is_none,
    is_nontrivial_with_statement,
    is_raises_call,
    is_raises_with,
)


class RaisesVisitor(Visitor[Config]):
    def _check_raises_call(self, node: ast.Call) -> None:
        """
        Checks for violations regarding `pytest.raises` call args (PT010 and PT011).
        """
        args = get_simple_call_args(node)
        exception = args.get_argument("expected_exception", position=0)
        if not exception:
            self.error_from_node(RaisesWithoutException, node)
            return

        exception_name = get_qualname(exception)
        if exception_name not in self.config.raises_require_match_for:
            return
        match = args.get_argument("match")
        if match is None or is_none(match) or is_empty_string(match):
            self.error_from_node(RaisesTooBroad, node, exception=exception_name)

    def _check_raises_with(self, node: ast.With) -> None:
        """Checks for PT012."""
        body = node.body

        is_complex_body = False
        if len(body) != 1:
            is_complex_body = True
        elif isinstance(
            body[0],
            (
                ast.If,
                ast.For,
                ast.AsyncFor,
                ast.While,
                ast.Try,
            ),
        ):
            is_complex_body = True
        elif is_nontrivial_with_statement(body[0]):
            is_complex_body = True

        if is_complex_body:
            self.error_from_node(RaisesWithMultipleStatements, node)

    def visit_Call(self, node: ast.Call) -> None:
        if is_raises_call(node):
            self._check_raises_call(node)

    def visit_With(self, node: ast.With) -> None:
        if is_raises_with(node):
            self._check_raises_with(node)

        self.generic_visit(node)


================================================
FILE: flake8_pytest_style/visitors/t_functions.py
================================================
from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import (
    FixtureParamWithoutValue,
    TFunctionArgumentWithDefault,
)
from flake8_pytest_style.utils import AnyFunctionDef, is_test_function


# This should be named `TestFunctionsVisitor` (and the module `test_functions`),
# but this way it is easier to avoid confusion with the `Test` prefix in pytest.
class TFunctionsVisitor(Visitor[Config]):
    def _check_test_function_args(self, node: AnyFunctionDef) -> None:
        """Checks for PT019, P028."""
        # intentionally not looking at posonlyargs because pytest passes everything
        # as kwargs, so declaring fixture args as positional-only will fail anyway
        for arg in node.args.args + node.args.kwonlyargs:
            if arg.arg.startswith("_"):
                # The error is raised at the position of `node` (function call),
                # not `arg`, to preserve backwards compatibility.
                self.error_from_node(FixtureParamWithoutValue, node, name=arg.arg)

        if node.args.defaults:
            pos_args = node.args.posonlyargs + node.args.args
            pos_args_with_defaults = pos_args[-len(node.args.defaults) :]  # noqa: E203
            for arg in pos_args_with_defaults:
                self.error_from_node(
                    TFunctionArgumentWithDefault, arg, name=node.name, arg=arg.arg
                )

        for arg, default in zip(node.args.kwonlyargs, node.args.kw_defaults):
            if default is not None:
                self.error_from_node(
                    TFunctionArgumentWithDefault, arg, name=node.name, arg=arg.arg
                )

    def visit_FunctionDef(self, node: AnyFunctionDef) -> None:
        if is_test_function(node):
            self._check_test_function_args(node)

        self.generic_visit(node)

    visit_AsyncFunctionDef = visit_FunctionDef  # noqa: N815


================================================
FILE: flake8_pytest_style/visitors/try_except.py
================================================
import ast
from typing import List, Optional

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import AssertInExcept


class TryExceptVisitor(Visitor[Config]):
    def __init__(self, config: Optional[Config] = None) -> None:
        super().__init__(config=config)
        self._exception_names: List[str] = []
        self._current_assert: Optional[ast.Assert] = None

    def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
        if node.name:
            self._exception_names.append(node.name)
        try:
            self.generic_visit(node)
        finally:
            if node.name:
                self._exception_names.pop()

    def visit_Assert(self, node: ast.Assert) -> None:
        self._current_assert = node
        try:
            self.visit(node.test)
        finally:
            self._current_assert = None

        if node.msg:
            self.visit(node.msg)

    def visit_Name(self, node: ast.Name) -> None:
        if self._current_assert:
            if node.id in self._exception_names:
                self.error_from_node(AssertInExcept, self._current_assert, name=node.id)


================================================
FILE: flake8_pytest_style/visitors/warns.py
================================================
import ast

from flake8_plugin_utils import Visitor

from flake8_pytest_style.config import Config
from flake8_pytest_style.errors import (
    WarnsTooBroad,
    WarnsWithMultipleStatements,
    WarnsWithoutException,
)
from flake8_pytest_style.utils import (
    get_qualname,
    get_simple_call_args,
    is_empty_string,
    is_none,
    is_nontrivial_with_statement,
    is_warns_call,
    is_warns_with,
)


class WarnsVisitor(Visitor[Config]):
    def _check_warns_call(self, node: ast.Call) -> None:
        """
        Checks for violations regarding `pytest.warns` call args (PT029 and PT030).
        """
        args = get_simple_call_args(node)
        warning = args.get_argument("expected_warning", position=0)
        if not warning:
            self.error_from_node(WarnsWithoutException, node)
            return

        warning_name = get_qualname(warning)
        if warning_name not in self.config.warns_require_match_for:
            return
        match = args.get_argument("match")
        if match is None or is_none(match) or is_empty_string(match):
            self.error_from_node(WarnsTooBroad, node, warning=warning_name)

    def _check_warns_with(self, node: ast.With) -> None:
        """Checks for PT031."""
        body = node.body

        is_complex_body = False
        if len(body) != 1:
            is_complex_body = True
        elif isinstance(
            body[0],
            (
                ast.If,
                ast.For,
                ast.AsyncFor,
                ast.While,
                ast.Try,
            ),
        ):
            is_complex_body = True
        elif is_nontrivial_with_statement(body[0]):
            is_complex_body = True

        if is_complex_body:
            self.error_from_node(WarnsWithMultipleStatements, node)

    def visit_Call(self, node: ast.Call) -> None:
        if is_warns_call(node):
            self._check_warns_call(node)

    def visit_With(self, node: ast.With) -> None:
        if is_warns_with(node):
            self._check_warns_with(node)

        self.generic_visit(node)


================================================
FILE: pyproject.toml
================================================
[project]
name = "flake8-pytest-style"
version = "2.2.0"
description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests."
authors = [
  { name = "Mikhail Burshteyn", email = "mdburshteyn@gmail.com" },
]
license = "MIT"
readme = 'README.md'
keywords = ["flake8", "pytest"]
dynamic = [
  "classifiers",
  "dependencies",
]
requires-python = ">=3.10"

[project.urls]
repository = "https://github.com/m-burst/flake8-pytest-style"
homepage = "https://pypi.org/project/flake8-pytest-style"

[tool.poetry]
classifiers = [
  "Development Status :: 5 - Production/Stable",
  "Environment :: Console",
  "Environment :: Plugins",
  "Framework :: Flake8",
  "Framework :: Pytest",
  "Intended Audience :: Developers",
  "Operating System :: OS Independent",
  "Topic :: Software Development :: Quality Assurance",
  "Topic :: Software Development :: Testing",
  "Topic :: Software Development :: Testing :: Unit",
]

[project.entry-points."flake8.extension"]
PT = 'flake8_pytest_style.plugin:PytestStylePlugin'

[tool.poetry.dependencies]
python = "^3.10"
flake8-plugin-utils = "^1.3.2"

[tool.poetry.group.dev.dependencies]
black = "^26.3.1"
bump2version = "^1.0.1"
mypy = ">=1.20.2"
pylint = "^4.0.5"
pytest = "^9.0.3"
pytest-cov = "^7.1.0"
pytest-deadfixtures = "^3.1"
flake8 = "^7.3.0"
pytest-mock = "^3.15.1"
tomlkit = ">=0.12.1,<0.15.0"

# The below group is taken from flake8-awesome with some exclusions
# that are irrelevant or unsupported.
# TODO remove Python version restriction after Bandit supports 3.14
# https://github.com/PyCQA/bandit/issues/1314
flake8-bandit = {version = "*", python="<3.14"}
flake8-breakpoint = "*"
flake8-bugbear = "*"
flake8-builtins = "*"
flake8-comprehensions = "*"
flake8-eradicate = "*"
flake8-if-expr = "*"
flake8-isort = "*"
flake8-print = "*"
flake8-requirements = "*"
pep8-naming = "*"

# broken on 3.14+
# flake8-annotations-complexity = "*"

# broken on 3.14+
# flake8-expression-complexity = "*"

# kinda broken on 3.12+ (needs pkg_resources), not needed here
# flake8-logging-format = "*"

# functionality implemented in this plugin
# flake8-pytest = "*"

# actually this plugin
# flake8-pytest-style = "*"

# broken on 3.14+, and probably not needed with mypy
# flake8-return = "*"

# end of flake8-awesome

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"


================================================
FILE: scripts/pypi_readme.py
================================================
#!/usr/bin/env python3
import pathlib
import re
from typing import Match, cast

import tomlkit
from tomlkit.container import Container


def process_readme(readme: str, project_metadata: Container) -> str:
    urls = cast(Container, project_metadata["urls"])
    repository = cast(str, urls["repository"])
    version = cast(str, project_metadata["version"])
    base_url = f"{repository}/blob/v{version}/"

    def replace_url(match: Match[str]) -> str:
        path_without_leading_slash = match.group(0).lstrip("/")
        return f"{base_url}{path_without_leading_slash}"

    return re.sub(r"/?\S+\.md", replace_url, readme)


def make_output_filename(input_filename: str) -> str:
    path = pathlib.Path(input_filename)
    return str(path.parent / f"{path.stem}-pypi{path.suffix}")


def main() -> None:
    with open("pyproject.toml") as pyproject_file:
        pyproject_data = tomlkit.loads(pyproject_file.read())
    project_metadata = cast(Container, pyproject_data["project"])

    readme_filename = cast(str, project_metadata["readme"])
    with open(readme_filename) as input_file:
        input_readme = input_file.read()

    output_readme = process_readme(input_readme, project_metadata)
    output_filename = make_output_filename(readme_filename)
    with open(output_filename, "w") as output_file:
        output_file.write(output_readme)

    project_metadata["readme"] = output_filename
    with open("pyproject.toml", "w") as pyproject_file:
        pyproject_file.write(tomlkit.dumps(pyproject_data))


if __name__ == "__main__":
    main()


================================================
FILE: setup.cfg
================================================
[flake8]
enable-extensions = G
exclude = .git, .venv
extend-ignore =
    ; 'id' is a python builtin, consider renaming the class attribute
    A003,
    ; line break before binary operator
    W503,
max-complexity = 10
max-line-length = 88
per-file-ignores =
    flake8_pytest_style/visitors/*.py:N802
    tests/*.py:S101
show-source = True

[mypy]
check_untyped_defs = True
disallow_any_generics = True
disallow_incomplete_defs = True
disallow_untyped_defs = True
ignore_missing_imports = True
no_implicit_optional = True

[mypy-tests.*]
disallow_incomplete_defs = False
disallow_untyped_defs = False

[isort]
balanced_wrapping = True
default_section = THIRDPARTY
include_trailing_comma = True
known_first_party = flake8_pytest_style, tests
line_length = 88
multi_line_output = 3

[pylint]
good-names = i,j,k,e,x,_,pk,id
max-args = 5
max-attributes = 10
max-bool-expr = 5
max-branches = 10
max-locals = 8
max-module-lines = 500
max-nested-blocks = 3
max-public-methods = 10
max-returns = 5
max-statements = 25
output-format = colorized

disable =
    C0103, ; Constant name "api" doesn't conform to UPPER_CASE naming style (invalid-name)
    C0111, ; Missing module docstring (missing-docstring)
    E0213, ; Method should have "self" as first argument (no-self-argument) - N805 for flake8
    R0801, ; Similar lines in 2 files (duplicate-code)
    R0901, ; Too many ancestors (m/n) (too-many-ancestors)
    R0903, ; Too few public methods (m/n) (too-few-public-methods)

ignored-classes =
    contextlib.closing,

[coverage:run]
omit = tests/*,**/__main__.py
branch = True

[coverage:report]
show_missing = True
skip_covered = True
fail_under = 90


================================================
FILE: tests/__init__.py
================================================


================================================
FILE: tests/conftest.py
================================================


================================================
FILE: tests/test_PT001_incorrect_fixture_parentheses_style.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import IncorrectFixtureParenthesesStyle
from flake8_pytest_style.visitors import FixturesVisitor

# make the configs independent of the actual default
_CONFIG_WITHOUT_PARENS = DEFAULT_CONFIG._replace(fixture_parentheses=False)
_CONFIG_WITH_PARENS = DEFAULT_CONFIG._replace(fixture_parentheses=True)


def test_ok_no_parameters():
    code = """
        import pytest

        @pytest.fixture()
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=_CONFIG_WITH_PARENS)


def test_ok_with_parameters():
    code = """
        import pytest

        @pytest.fixture(scope='module')
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=_CONFIG_WITH_PARENS)


def test_ok_without_parens():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=_CONFIG_WITHOUT_PARENS)


def test_error_without_parens():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        IncorrectFixtureParenthesesStyle,
        config=_CONFIG_WITH_PARENS,
        expected_parens="()",
        actual_parens="",
    )


def test_error_with_parens():
    code = """
        import pytest

        @pytest.fixture()
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        IncorrectFixtureParenthesesStyle,
        config=_CONFIG_WITHOUT_PARENS,
        expected_parens="",
        actual_parens="()",
    )


================================================
FILE: tests/test_PT002_fixture_positional_args.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import FixturePositionalArgs
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_no_args():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_only_kwargs():
    code = """
        import pytest

        @pytest.fixture(scope='module')
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error_only_args():
    code = """
        import pytest

        @pytest.fixture('module')
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        FixturePositionalArgs,
        name="my_fixture",
        config=DEFAULT_CONFIG,
    )


def test_error_mixed():
    code = """
        import pytest

        @pytest.fixture('module', autouse=True)
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        FixturePositionalArgs,
        name="my_fixture",
        config=DEFAULT_CONFIG,
    )


================================================
FILE: tests/test_PT003_extraneous_scope_function.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import ExtraneousScopeFunction
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_no_scope():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_other_scope():
    code = """
        import pytest

        @pytest.fixture(scope='module')
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error():
    code = """
        import pytest

        @pytest.fixture(scope='function')
        def my_fixture():
            return 0
    """
    assert_error(FixturesVisitor, code, ExtraneousScopeFunction, config=DEFAULT_CONFIG)


================================================
FILE: tests/test_PT004_missing_fixture_name_underscore.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import MissingFixtureNameUnderscore
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_simple():
    code = """
        import pytest

        @pytest.fixture
        def _patch_something(mocker):
            mocker.patch('some.thing')
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_with_return():
    code = """
        import pytest

        @pytest.fixture
        def _patch_something(mocker):
            if something:
                return
            mocker.patch('some.thing')
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_with_yield():
    code = """
        import pytest

        @pytest.fixture
        def _activate_context():
            with context:
                yield
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_abstract_with_import_abc():
    code = """
        import abc

        import pytest

        class BaseTest:
            @pytest.fixture
            @abc.abstractmethod
            def my_fixture():
                raise NotImplementedError
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_abstract_with_from_import():
    code = """
        from abc import abstractmethod

        import pytest

        class BaseTest:
            @pytest.fixture
            @abstractmethod
            def my_fixture():
                raise NotImplementedError
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_ignoring_yield_from():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            yield from some_generator()
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error_simple():
    code = """
        import pytest

        @pytest.fixture
        def patch_something(mocker):
            mocker.patch('some.thing')
    """
    assert_error(
        FixturesVisitor,
        code,
        MissingFixtureNameUnderscore,
        name="patch_something",
        config=DEFAULT_CONFIG,
    )


def test_error_with_yield():
    code = """
        import pytest

        @pytest.fixture
        def activate_context():
            with context:
                yield
    """
    assert_error(
        FixturesVisitor,
        code,
        MissingFixtureNameUnderscore,
        name="activate_context",
        config=DEFAULT_CONFIG,
    )


================================================
FILE: tests/test_PT005_incorrect_fixture_name_underscore.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import IncorrectFixtureNameUnderscore
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_with_return():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture(mocker):
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_with_yield():
    code = """
        import pytest

        @pytest.fixture
        def activate_context():
            with get_context() as context:
                yield context
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_nested_function():
    code = """
        @pytest.fixture
        def _any_fixture(mocker):
            def nested_function():
                return 1

            mocker.patch('...', nested_function)
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_abstract_with_import_abc():
    code = """
        import abc

        import pytest

        class BaseTest:
            @pytest.fixture
            @abc.abstractmethod
            def _my_fixture():
                return NotImplemented
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_abstract_with_from_import():
    code = """
        from abc import abstractmethod

        import pytest

        class BaseTest:
            @pytest.fixture
            @abstractmethod
            def _my_fixture():
                return NotImplemented
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error_with_return():
    code = """
        import pytest

        @pytest.fixture
        def _my_fixture(mocker):
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        IncorrectFixtureNameUnderscore,
        name="_my_fixture",
        config=DEFAULT_CONFIG,
    )


def test_error_with_yield():
    code = """
        import pytest

        @pytest.fixture
        def _activate_context():
            with get_context() as context:
                yield context
    """
    assert_error(
        FixturesVisitor,
        code,
        IncorrectFixtureNameUnderscore,
        name="_activate_context",
        config=DEFAULT_CONFIG,
    )


def test_error_with_conditional_yield_from():
    code = """
        import pytest

        @pytest.fixture
        def _activate_context():
            if some_condition:
                with get_context() as context:
                    yield context
            else:
                yield from other_context()
    """
    # since we have yield with value in one branch,
    # we assume that the fixture yields a value
    assert_error(
        FixturesVisitor,
        code,
        IncorrectFixtureNameUnderscore,
        name="_activate_context",
        config=DEFAULT_CONFIG,
    )


================================================
FILE: tests/test_PT006_parametrize_names_wrong_type.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG, ParametrizeNamesType
from flake8_pytest_style.errors import ParametrizeNamesWrongType
from flake8_pytest_style.visitors import ParametrizeVisitor

NAMES_CSV = "name1,name2"
NAMES_LIST = ["name1", "name2"]
NAMES_TUPLE = ("name1", "name2")


def test_ok_single():
    code = """
        import pytest

        pytest.mark.parametrize(
            'name',
            ['a', 'b', 'c'],
        )
    """
    assert_not_error(ParametrizeVisitor, code, config=DEFAULT_CONFIG)


@pytest.mark.parametrize(
    ("cfg_type", "names"),
    [
        (ParametrizeNamesType.CSV, NAMES_CSV),
        (ParametrizeNamesType.LIST, NAMES_LIST),
        (ParametrizeNamesType.TUPLE, NAMES_TUPLE),
    ],
)
def test_ok_multiple(cfg_type, names):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            {names!r},
            [
                ('a', 'b'),
                ('c', 'd'),
            ],
        )
    """
    config = DEFAULT_CONFIG._replace(parametrize_names_type=cfg_type)
    assert_not_error(ParametrizeVisitor, code, config=config)


def test_error_single_tuple():
    code = """
        import pytest

        pytest.mark.parametrize(
            ('name',),
            ['a', 'b', 'c'],
        )
    """
    assert_error(
        ParametrizeVisitor,
        code,
        ParametrizeNamesWrongType,
        expected_type="string",
        config=DEFAULT_CONFIG,
    )


@pytest.mark.parametrize(
    ("cfg_type", "names"),
    [
        (ParametrizeNamesType.CSV, NAMES_LIST),
        (ParametrizeNamesType.CSV, NAMES_TUPLE),
        (ParametrizeNamesType.LIST, NAMES_CSV),
        (ParametrizeNamesType.LIST, NAMES_TUPLE),
        (ParametrizeNamesType.TUPLE, NAMES_CSV),
        (ParametrizeNamesType.TUPLE, NAMES_LIST),
    ],
)
def test_error_multiple(cfg_type, names):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            {names!r},
            [
                ('a', 'b'),
                ('c', 'd'),
            ],
        )
    """
    config = DEFAULT_CONFIG._replace(parametrize_names_type=cfg_type)
    assert_error(
        ParametrizeVisitor,
        code,
        ParametrizeNamesWrongType,
        expected_type=cfg_type.value,
        config=config,
    )


================================================
FILE: tests/test_PT007_parametrize_values_wrong_type.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import (
    DEFAULT_CONFIG,
    ParametrizeValuesRowType,
    ParametrizeValuesType,
)
from flake8_pytest_style.errors import ParametrizeValuesWrongType
from flake8_pytest_style.visitors import ParametrizeVisitor

VALUES_LIST = ["a", "b", "c"]
VALUES_TUPLE = ("a", "b", "c")
VALUES_LIST_OF_LISTS = [["a", "b"], ["c", "d"]]
VALUES_LIST_OF_TUPLES = [("a", "b"), ("c", "d")]
VALUES_TUPLE_OF_LISTS = (["a", "b"], ["c", "d"])
VALUES_TUPLE_OF_TUPLES = (("a", "b"), ("c", "d"))
VALUES_LIST_OF_MIXED = [["a", "b"], ("c", "d")]
VALUES_TUPLE_OF_MIXED = (["a", "b"], ("c", "d"))


def _get_expected_type_str(values_cfg_type, rows_cfg_type):
    return f"{values_cfg_type.value} of {rows_cfg_type.value}s"


@pytest.mark.parametrize(
    ("values_cfg_type", "values"),
    [
        (ParametrizeValuesType.LIST, VALUES_LIST),
        (ParametrizeValuesType.TUPLE, VALUES_TUPLE),
    ],
)
def test_ok_single(values_cfg_type, values):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            'name',
            {values!r},
        )
    """
    config = DEFAULT_CONFIG._replace(parametrize_values_type=values_cfg_type)
    assert_not_error(ParametrizeVisitor, code, config=config)


@pytest.mark.parametrize(
    ("values_cfg_type", "rows_cfg_type", "values"),
    [
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.LIST,
            VALUES_LIST_OF_LISTS,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.LIST,
            VALUES_TUPLE_OF_LISTS,
        ),
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.TUPLE,
            VALUES_LIST_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.TUPLE,
            VALUES_TUPLE_OF_TUPLES,
        ),
    ],
)
def test_ok_multiple(values_cfg_type, rows_cfg_type, values):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            ('name1', 'name2'),
            {values!r},
        )
    """
    config = DEFAULT_CONFIG._replace(
        parametrize_values_type=values_cfg_type,
        parametrize_values_row_type=rows_cfg_type,
    )
    assert_not_error(ParametrizeVisitor, code, config=config)


@pytest.mark.parametrize(
    ("values_cfg_type", "values"),
    [
        (ParametrizeValuesType.LIST, VALUES_TUPLE),
        (ParametrizeValuesType.TUPLE, VALUES_LIST),
    ],
)
def test_error_single(values_cfg_type, values):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            'name',
            {values!r},
        )
    """
    config = DEFAULT_CONFIG._replace(parametrize_values_type=values_cfg_type)
    assert_error(
        ParametrizeVisitor,
        code,
        ParametrizeValuesWrongType,
        expected_type=values_cfg_type.value,
        config=config,
    )


def test_error_single_tuple_as_decorator():
    code = """
        import pytest

        @pytest.mark.parametrize(
            'name',
            ('a', 'b', 'c'),
        )
        def test_smth(name):
            pass
    """
    assert_error(
        ParametrizeVisitor,
        code,
        ParametrizeValuesWrongType,
        expected_type="list",
        config=DEFAULT_CONFIG,
    )


@pytest.mark.parametrize(
    ("values_cfg_type", "rows_cfg_type", "values"),
    [
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.LIST,
            VALUES_LIST_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.LIST,
            VALUES_TUPLE_OF_LISTS,
        ),
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.LIST,
            VALUES_TUPLE_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.LIST,
            VALUES_LIST_OF_LISTS,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.LIST,
            VALUES_LIST_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.LIST,
            VALUES_TUPLE_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.TUPLE,
            VALUES_LIST_OF_LISTS,
        ),
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.TUPLE,
            VALUES_TUPLE_OF_LISTS,
        ),
        (
            ParametrizeValuesType.LIST,
            ParametrizeValuesRowType.TUPLE,
            VALUES_TUPLE_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.TUPLE,
            VALUES_LIST_OF_LISTS,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.TUPLE,
            VALUES_LIST_OF_TUPLES,
        ),
        (
            ParametrizeValuesType.TUPLE,
            ParametrizeValuesRowType.TUPLE,
            VALUES_TUPLE_OF_LISTS,
        ),
    ],
)
def test_error_multiple(values_cfg_type, rows_cfg_type, values):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            ('name1', 'name2'),
            {values!r},
        )
    """
    config = DEFAULT_CONFIG._replace(
        parametrize_values_type=values_cfg_type,
        parametrize_values_row_type=rows_cfg_type,
    )
    assert_error(
        ParametrizeVisitor,
        code,
        ParametrizeValuesWrongType,
        expected_type=_get_expected_type_str(values_cfg_type, rows_cfg_type),
        config=config,
    )


@pytest.mark.parametrize("values_cfg_type", list(ParametrizeValuesType))
@pytest.mark.parametrize("rows_cfg_type", list(ParametrizeValuesRowType))
@pytest.mark.parametrize("values", [VALUES_LIST_OF_MIXED, VALUES_TUPLE_OF_MIXED])
def test_error_multiple_mixed(values_cfg_type, rows_cfg_type, values):
    code = f"""
        import pytest

        pytest.mark.parametrize(
            ('name1', 'name2'),
            {values!r},
        )
    """
    config = DEFAULT_CONFIG._replace(
        parametrize_values_type=values_cfg_type,
        parametrize_values_row_type=rows_cfg_type,
    )
    assert_error(
        ParametrizeVisitor,
        code,
        ParametrizeValuesWrongType,
        expected_type=_get_expected_type_str(values_cfg_type, rows_cfg_type),
        config=config,
    )


================================================
FILE: tests/test_PT008_patch_with_lambda.py
================================================
import sys

import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.errors import PatchWithLambda
from flake8_pytest_style.visitors import PatchVisitor

HAS_POSITIONAL_ONLY_ARGS = sys.version_info >= (3, 8)

parametrize_code_template = pytest.mark.parametrize(
    "code_template",
    [
        "mocker.patch('module.name', {})",
        "module_mocker.patch('module.name', {})",
        "mocker.patch.object(obj, 'attr', {})",
        "module_mocker.patch.object(obj, 'attr', {})",
    ],
    ids=(
        "mocker.patch",
        "module_mocker.patch",
        "mocker.patch.object",
        "module_mocker.patch.object",
    ),
)


@parametrize_code_template
@pytest.mark.parametrize(
    "patch_with",
    [
        "not_lambda",
        "return_value=None",
        "lambda x, y: x",
        "lambda *args: args",
        "lambda **kwargs: kwargs",
        pytest.param(
            "lambda x, /, y: x",
            marks=[
                pytest.mark.skipif(
                    not HAS_POSITIONAL_ONLY_ARGS, reason=f"unsupported in {sys.version}"
                )
            ],
        ),
    ],
)
def test_ok(code_template, patch_with):
    code = code_template.format(patch_with)
    assert_not_error(PatchVisitor, code)


@parametrize_code_template
@pytest.mark.parametrize(
    "patch_with", ["lambda: None", "lambda x, y: None", "lambda *args, **kwargs: None"]
)
def test_error(code_template, patch_with):
    code = code_template.format(patch_with)
    assert_error(PatchVisitor, code, PatchWithLambda)


================================================
FILE: tests/test_PT009_unittest_assertion.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.errors import UnittestAssertion
from flake8_pytest_style.visitors import UnittestAssertionVisitor


def test_ok_no_parameters():
    code = """
        import pytest

        def test_xxx():
            assert 1 == 1
    """
    assert_not_error(UnittestAssertionVisitor, code)


def test_error():
    code = """
        import pytest

        def test_xxx():
            self.assertEqual(1, 1)
    """
    assert_error(
        UnittestAssertionVisitor, code, UnittestAssertion, assertion="assertEqual"
    )


================================================
FILE: tests/test_PT010_raises_without_exception.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import RaisesWithoutException
from flake8_pytest_style.visitors import RaisesVisitor


def test_ok():
    code = """
        import pytest

        def test_something():
            with pytest.raises(UnicodeError):
                pass
    """
    assert_not_error(RaisesVisitor, code, config=DEFAULT_CONFIG)


def test_error():
    code = """
        import pytest

        def test_something():
            with pytest.raises():
                pass
    """
    assert_error(RaisesVisitor, code, RaisesWithoutException, config=DEFAULT_CONFIG)


================================================
FILE: tests/test_PT011_raises_too_broad.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import RaisesTooBroad
from flake8_pytest_style.visitors import RaisesVisitor


def test_ok():
    code = """
        import pytest

        def test_something():
            with pytest.raises(ValueError, match="Can't divide by 0"):
                raise ValueError("Can't divide by 0")
    """
    assert_not_error(RaisesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_different_error_from_config():
    code = """
        import pytest

        def test_something():
            with pytest.raises(ZeroDivisionError):
                raise ZeroDivisionError("Can't divide by 0")
    """
    assert_not_error(RaisesVisitor, code, config=DEFAULT_CONFIG)


@pytest.mark.parametrize("exception", ["ValueError", "socket.error"])
def test_error_no_argument_given(exception):
    code = f"""
        import pytest

        def test_something():
            with pytest.raises({exception}):
                raise ValueError("Can't divide 1 by 0")
    """
    assert_error(
        RaisesVisitor,
        code,
        RaisesTooBroad,
        config=DEFAULT_CONFIG,
        exception=exception,
    )


@pytest.mark.parametrize("match", ["None", '""', 'f""'])
def test_error_match_is_empty(match):
    code = f"""
        import pytest

        def test_something():
            with pytest.raises(ValueError, match={match}):
                raise ValueError("Can't divide 1 by 0")
    """
    assert_error(
        RaisesVisitor,
        code,
        RaisesTooBroad,
        config=DEFAULT_CONFIG,
        exception="ValueError",
    )


================================================
FILE: tests/test_PT012_raises_with_multiple_statements.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import RaisesWithMultipleStatements
from flake8_pytest_style.visitors import RaisesVisitor


def test_ok():
    code = """
        def test_something():
            with pytest.raises(AttributeError):
                [].size
    """
    assert_not_error(RaisesVisitor, code, config=DEFAULT_CONFIG)


@pytest.mark.parametrize("maybe_async", ["", "async "])
def test_ok_trivial_with(maybe_async):
    code = f"""
        async def test_something():
            with pytest.raises(AttributeError):
                {maybe_async}with context_manager_under_test():
                    pass
    """
    assert_not_error(RaisesVisitor, code, config=DEFAULT_CONFIG)


def test_error_multiple_statements():
    code = """
        def test_something():
            with pytest.raises(AttributeError):
                len([])
                [].size
    """
    assert_error(
        RaisesVisitor, code, RaisesWithMultipleStatements, config=DEFAULT_CONFIG
    )


@pytest.mark.parametrize(
    "statement", ["if", "for i in", "async for i in", "while", "with", "async with"]
)
def test_error_complex_statement(statement):
    code = f"""
        async def test_something():
            with pytest.raises(AttributeError):
                {statement} True:
                    [].size
    """
    assert_error(
        RaisesVisitor, code, RaisesWithMultipleStatements, config=DEFAULT_CONFIG
    )


def test_error_try():
    code = """
        def test_something():
            with pytest.raises(AttributeError):
                try:
                    [].size
                except:
                    raise
    """
    assert_error(
        RaisesVisitor, code, RaisesWithMultipleStatements, config=DEFAULT_CONFIG
    )


================================================
FILE: tests/test_PT013_incorrect_pytest_import.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.errors import IncorrectPytestImport
from flake8_pytest_style.visitors.imports import ImportsVisitor


@pytest.mark.parametrize(
    "code",
    [
        "import pytest",
        "import pytest as pytest",
        "from notpytest import fixture",
        "from . import fixture",
        "from .pytest import fixture",
    ],
)
def test_ok(code):
    assert_not_error(ImportsVisitor, code)


@pytest.mark.parametrize(
    "code",
    [
        "import pytest as other_name",
        "from pytest import fixture",
        "from pytest import fixture as other_name",
    ],
)
def test_error(code):
    assert_error(ImportsVisitor, code, IncorrectPytestImport)


================================================
FILE: tests/test_PT014_duplicate_parametrize_test_cases.py
================================================
from flake8_plugin_utils import assert_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import DuplicateParametrizeTestCases
from flake8_pytest_style.visitors import ParametrizeVisitor


def test_error():
    code = """
        pytest.mark.parametrize(
            'name',
            [
                {1: 2, 3: {*other_set, 4, 5}, **otherdict},
                {x: y},
                {3: {5, *other_set, 4}, 1: 2, **otherdict},
            ]
        )
    """
    assert_error(
        ParametrizeVisitor,
        code,
        DuplicateParametrizeTestCases,
        indexes=(1, 3),
        config=DEFAULT_CONFIG,
    )


================================================
FILE: tests/test_PT015_assert_always_false.py
================================================
import pytest
from flake8_plugin_utils.utils import assert_error, assert_not_error

from flake8_pytest_style.errors import AssertAlwaysFalse
from flake8_pytest_style.visitors import FailVisitor


def test_ok():
    code = """
        def test_xxx():
            assert [0]
    """
    assert_not_error(FailVisitor, code)


@pytest.mark.parametrize(
    "falsy_constant",
    [
        "None",
        "False",
        "0",
        "0.0",
        '""',
        'f""',
        "[]",
        "()",
        "{}",
        "list()",
        "set()",
        "tuple()",
        "dict()",
        "frozenset()",
        "list([])",
        "set(set())",
        'tuple("")',
    ],
)
def test_error(falsy_constant):
    code = f"""
        def test_xxx():
            assert {falsy_constant}
    """
    assert_error(FailVisitor, code, AssertAlwaysFalse)


================================================
FILE: tests/test_PT016_fail_without_message.py
================================================
import pytest
from flake8_plugin_utils.utils import assert_error, assert_not_error

from flake8_pytest_style.errors import FailWithoutMessage
from flake8_pytest_style.visitors import FailVisitor


def test_ok_arg():
    code = """
        def test_xxx():
            pytest.fail('this is a failure')
    """
    assert_not_error(FailVisitor, code)


def test_ok_kwarg():
    code = """
        def test_xxx():
            pytest.fail(reason='this is a failure')
    """
    assert_not_error(FailVisitor, code)


def test_ok_kwarg_legacy():
    code = """
        def test_xxx():
            pytest.fail(msg='this is a failure')
    """
    assert_not_error(FailVisitor, code)


@pytest.mark.parametrize(
    "args", ["", '""', 'f""', 'reason=""', 'reason=f""', 'msg=""', 'msg=f""']
)
def test_error(args):
    code = f"""
        def test_xxx():
            pytest.fail({args})
    """
    assert_error(FailVisitor, code, FailWithoutMessage)


================================================
FILE: tests/test_PT017_assert_in_except.py
================================================
from flake8_plugin_utils.utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import AssertInExcept
from flake8_pytest_style.visitors import TryExceptVisitor


def test_ok():
    code = """
        def test_xxx():
            try:
                something()
            except Exception as e:
                something_else()

            with pytest.raises(ZeroDivisionError) as e:
                1 / 0
            assert e.value.message
    """
    assert_not_error(TryExceptVisitor, code, config=DEFAULT_CONFIG)


def test_error():
    code = """
        def test_xxx():
            try:
                something()
            except Exception as e:
                assert e.message, 'blah blah'
    """
    assert_error(
        TryExceptVisitor, code, AssertInExcept, name="e", config=DEFAULT_CONFIG
    )


================================================
FILE: tests/test_PT018_composite_assertion.py
================================================
import pytest
from flake8_plugin_utils.utils import assert_error, assert_not_error

from flake8_pytest_style.errors import CompositeAssertion
from flake8_pytest_style.visitors.assertion import AssertionVisitor


def test_ok():
    code = """
        def test_xxx():
            assert something
            assert something or something_else
            assert something or something_else and something_third
            assert not (something and something_else)
    """
    assert_not_error(AssertionVisitor, code)


@pytest.mark.parametrize(
    "condition",
    [
        "something and something_else",
        "something and something_else and something_third",
        "something and not something_else",
        "something and (something_else or something_third)",
        "not (something or something_else)",
        "not (something or something_else or something_third)",
        "not (something or something_else and something_third)",
    ],
)
def test_error(condition):
    code = f"""
        def test_xxx():
            assert {condition}
    """
    assert_error(AssertionVisitor, code, CompositeAssertion)


================================================
FILE: tests/test_PT019_fixture_param_without_value.py
================================================
from flake8_plugin_utils.utils import assert_error, assert_not_error

from flake8_pytest_style.errors import FixtureParamWithoutValue
from flake8_pytest_style.visitors import TFunctionsVisitor


def test_ok_good_param_name():
    code = """
        def test_xxx(fixture):
            pass
    """
    assert_not_error(TFunctionsVisitor, code)


def test_ok_non_test_function():
    code = """
        def xxx(_param):
            pass
    """
    assert_not_error(TFunctionsVisitor, code)


def test_error_arg():
    code = """
        def test_xxx(_fixture):
            pass
    """
    assert_error(TFunctionsVisitor, code, FixtureParamWithoutValue, name="_fixture")


def test_error_kwonly():
    code = """
        def test_xxx(*, _fixture):
            pass
    """
    assert_error(TFunctionsVisitor, code, FixtureParamWithoutValue, name="_fixture")


================================================
FILE: tests/test_PT020_deprecated_yield_fixture.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import DeprecatedYieldFixture
from flake8_pytest_style.visitors import FixturesVisitor

# make the configs independent of the actual default
_CONFIG_WITHOUT_PARENS = DEFAULT_CONFIG._replace(fixture_parentheses=False)
_CONFIG_WITH_PARENS = DEFAULT_CONFIG._replace(fixture_parentheses=True)


def test_ok_no_parameters():
    code = """
        import pytest

        @pytest.fixture()
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=_CONFIG_WITH_PARENS)


def test_ok_without_parens():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=_CONFIG_WITHOUT_PARENS)


def test_error_without_parens():
    code = """
        import pytest

        @pytest.yield_fixture
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor, code, DeprecatedYieldFixture, config=_CONFIG_WITHOUT_PARENS
    )


def test_error_with_parens():
    code = """
        import pytest

        @pytest.yield_fixture()
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor, code, DeprecatedYieldFixture, config=_CONFIG_WITH_PARENS
    )


================================================
FILE: tests/test_PT021_fixture_finalizer_callback.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import FixtureFinalizerCallback
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_return():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_yield():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            resource = acquire_resource()
            yield resource
            resource.release()
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_other_request():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            request = get_request()
            request.addfinalizer(finalizer)
            return request
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_other_function():
    # factory fixtures are a tricky use-case where addfinalizer cannot be replaced
    # inspired by tls_http_server fixture in cherrypy/cheroot
    code = """
        import functools
        import pytest

        def create_resource(arg, request):
            resource = Resource(arg)
            request.addfinalizer(resource.release)
            return resource

        @pytest.fixture
        def resource_factory(request):
            return functools.partial(create_resource, request=request)
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_nested_function():
    # https://github.com/m-burst/flake8-pytest-style/issues/46
    code = """
        import pytest

        @pytest.fixture
        def resource_factory(request):
            def create_resource(arg) -> Resource:
                resource = Resource(arg)
                request.addfinalizer(resource.release)
                return resource
            return create_resource
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error_return():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture(request):
            resource = acquire_resource()
            request.addfinalizer(resource.release)
            return resource
    """
    assert_error(FixturesVisitor, code, FixtureFinalizerCallback, config=DEFAULT_CONFIG)


def test_error_yield():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture(request):
            resource = acquire_resource()
            request.addfinalizer(resource.release)
            yield resource
            resource  # prevent PT022
    """
    assert_error(FixturesVisitor, code, FixtureFinalizerCallback, config=DEFAULT_CONFIG)


================================================
FILE: tests/test_PT022_useless_yield_fixture.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import UselessYieldFixture
from flake8_pytest_style.visitors import FixturesVisitor

# Skipping basic OK tests because we have A LOT of valid fixtures in other
# test files


def test_ok_complex_logic():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            if some_condition:
                resource = acquire_resource()
                yield resource
                resource.release()
                return
            yield None
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error():
    code = """
        import pytest

        @pytest.fixture
        def my_fixture():
            resource = acquire_resource()
            yield resource
    """
    assert_error(
        FixturesVisitor,
        code,
        UselessYieldFixture,
        name="my_fixture",
        config=DEFAULT_CONFIG,
    )


================================================
FILE: tests/test_PT023_incorrect_mark_parentheses_style.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import IncorrectMarkParenthesesStyle
from flake8_pytest_style.visitors import MarksVisitor

_CONFIG_WITHOUT_PARENS = DEFAULT_CONFIG._replace(mark_parentheses=False)
_CONFIG_WITH_PARENS = DEFAULT_CONFIG._replace(mark_parentheses=True)


SAMPLES_WITHOUT_PARENTHESES = [
    pytest.param(
        """
            import pytest

            @pytest.mark.foo
            def test_something():
                pass
        """,
        id="function",
    ),
    pytest.param(
        """
            import pytest

            @pytest.mark.foo
            class TestClass:
                def test_something():
                    pass
        """,
        id="class",
    ),
    pytest.param(
        """
            import pytest

            class TestClass:
                @pytest.mark.foo
                def test_something():
                    pass
        """,
        id="method",
    ),
    pytest.param(
        """
            import pytest

            class TestClass:
                @pytest.mark.foo
                class TestNestedClass:
                    def test_something():
                        pass
        """,
        id="nested_class",
    ),
    pytest.param(
        """
            import pytest

            class TestClass:
                class TestNestedClass:
                    @pytest.mark.foo
                    def test_something():
                        pass
        """,
        id="nested_class_method",
    ),
]

SAMPLES_WITH_PARENTHESES = [
    pytest.param(
        """
            import pytest

            @pytest.mark.foo()
            def test_something():
                pass
        """,
        id="function",
    ),
    pytest.param(
        """
            import pytest

            @pytest.mark.foo()
            class TestClass:
                def test_something():
                    pass
        """,
        id="class",
    ),
    pytest.param(
        """
            import pytest

            class TestClass:
                @pytest.mark.foo()
                def test_something():
                    pass
        """,
        id="method",
    ),
    pytest.param(
        """
            import pytest

            class TestClass:
                @pytest.mark.foo()
                class TestNestedClass:
                    def test_something():
                        pass
        """,
        id="nested_class",
    ),
    pytest.param(
        """
            import pytest

            class TestClass:
                class TestNestedClass:
                    @pytest.mark.foo()
                    def test_something():
                        pass
        """,
        id="nested_class_method",
    ),
]


@pytest.mark.parametrize("mark_parentheses", [True, False])
def test_ok_with_parameters_regardless_of_config(mark_parentheses: bool):
    code = """
        import pytest

        @pytest.mark.foo(scope='module')
        def test_something():
            pass
    """
    config = DEFAULT_CONFIG._replace(mark_parentheses=mark_parentheses)
    assert_not_error(MarksVisitor, code, config=config)


@pytest.mark.parametrize("code", SAMPLES_WITHOUT_PARENTHESES)
def test_ok_without_parens(code: str):
    assert_not_error(MarksVisitor, code, config=_CONFIG_WITHOUT_PARENS)


@pytest.mark.parametrize("code", SAMPLES_WITH_PARENTHESES)
def test_ok_with_parens(code: str):
    assert_not_error(MarksVisitor, code, config=_CONFIG_WITH_PARENS)


@pytest.mark.parametrize("code", SAMPLES_WITHOUT_PARENTHESES)
def test_error_without_parens(code: str):
    assert_error(
        MarksVisitor,
        code,
        IncorrectMarkParenthesesStyle,
        config=_CONFIG_WITH_PARENS,
        mark_name="foo",
        expected_parens="()",
        actual_parens="",
    )


@pytest.mark.parametrize("code", SAMPLES_WITH_PARENTHESES)
def test_error_with_parens(code: str):
    assert_error(
        MarksVisitor,
        code,
        IncorrectMarkParenthesesStyle,
        config=_CONFIG_WITHOUT_PARENS,
        mark_name="foo",
        expected_parens="",
        actual_parens="()",
    )


================================================
FILE: tests/test_PT024_unncessary_asyncio_mark_on_fixture.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import UnnecessaryAsyncioMarkOnFixture
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_not_fixture():
    code = """
        import pytest

        @pytest.mark.asyncio()
        async def test_something():
            pass
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_ok_not_fixture_no_parens():
    code = """
        import pytest

        @pytest.mark.asyncio
        async def test_something():
            pass
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error_before():
    code = """
        import pytest

        @pytest.mark.asyncio()
        @pytest.fixture
        async def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor, code, UnnecessaryAsyncioMarkOnFixture, config=DEFAULT_CONFIG
    )


def test_error_before_no_parens():
    code = """
        import pytest

        @pytest.mark.asyncio
        @pytest.fixture
        async def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor, code, UnnecessaryAsyncioMarkOnFixture, config=DEFAULT_CONFIG
    )


def test_error_after():
    code = """
        import pytest

        @pytest.fixture
        @pytest.mark.asyncio()
        async def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor, code, UnnecessaryAsyncioMarkOnFixture, config=DEFAULT_CONFIG
    )


def test_error_after_no_parens():
    code = """
        import pytest

        @pytest.fixture
        @pytest.mark.asyncio
        async def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor, code, UnnecessaryAsyncioMarkOnFixture, config=DEFAULT_CONFIG
    )


================================================
FILE: tests/test_PT025_erroneous_usefixtures_on_fixture.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import ErroneousUseFixturesOnFixture
from flake8_pytest_style.visitors import FixturesVisitor


def test_ok_not_fixture():
    code = """
        import pytest

        @pytest.mark.usefixtures('a')
        def test_something():
            pass
    """
    assert_not_error(FixturesVisitor, code, config=DEFAULT_CONFIG)


def test_error_before():
    code = """
        import pytest

        @pytest.mark.usefixtures('a')
        @pytest.fixture
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        ErroneousUseFixturesOnFixture,
        config=DEFAULT_CONFIG,
    )


def test_error_after():
    code = """
        import pytest

        @pytest.fixture
        @pytest.mark.usefixtures('a')
        def my_fixture():
            return 0
    """
    assert_error(
        FixturesVisitor,
        code,
        ErroneousUseFixturesOnFixture,
        config=DEFAULT_CONFIG,
    )


================================================
FILE: tests/test_PT026_usefixtures_without_parameters.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import UseFixturesWithoutParameters
from flake8_pytest_style.visitors import MarksVisitor


def test_ok():
    code = """
        import pytest

        @pytest.mark.usefixtures('a')
        def test_something():
            pass
    """
    assert_not_error(MarksVisitor, code, config=DEFAULT_CONFIG)


def test_ok_another_mark_with_parens():
    code = """
        import pytest

        @pytest.mark.foo()
        def test_something():
            pass
    """
    config = DEFAULT_CONFIG._replace(mark_parentheses=True)
    assert_not_error(MarksVisitor, code, config=config)


def test_ok_another_mark_no_parens():
    code = """
        import pytest

        @pytest.mark.foo
        def test_something():
            pass
    """
    config = DEFAULT_CONFIG._replace(mark_parentheses=False)
    assert_not_error(MarksVisitor, code, config=config)


def test_error_with_parens():
    code = """
        import pytest

        @pytest.mark.usefixtures()
        def test_something():
            pass
    """
    config = DEFAULT_CONFIG._replace(mark_parentheses=True)
    assert_error(MarksVisitor, code, UseFixturesWithoutParameters, config=config)


def test_error_no_parens():
    code = """
        import pytest

        @pytest.mark.usefixtures
        def test_something():
            pass
    """
    config = DEFAULT_CONFIG._replace(mark_parentheses=False)
    assert_error(MarksVisitor, code, UseFixturesWithoutParameters, config=config)


================================================
FILE: tests/test_PT027_unittest_raises_assertion.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.errors import UnittestRaisesAssertion
from flake8_pytest_style.visitors import UnittestAssertionVisitor


def test_ok_no_parameters():
    code = """
        import pytest

        def test_xxx():
            with pytest.raises(ValueError):
                raise ValueError()
    """
    assert_not_error(UnittestAssertionVisitor, code)


def test_error():
    code = """
        import pytest

        def test_xxx():
            with self.assertRaises(ValueError):
                raise ValueError()
    """
    assert_error(
        UnittestAssertionVisitor,
        code,
        UnittestRaisesAssertion,
        assertion="assertRaises",
    )


================================================
FILE: tests/test_PT028_test_function_argument_with_default.py
================================================
import ast
import textwrap

from flake8_plugin_utils.utils import assert_error, assert_not_error

from flake8_pytest_style.errors import TFunctionArgumentWithDefault
from flake8_pytest_style.visitors import TFunctionsVisitor


def test_ok():
    code = """
        def test_xxx(fixture1, /, fixture2, *, fixture3):
            pass
    """
    assert_not_error(TFunctionsVisitor, code)


def test_ok_non_test_function():
    code = """
        def xxx(posonly1, posonly2=2, /, arg=3, *, kwonly=4):
            pass
    """
    assert_not_error(TFunctionsVisitor, code)


def test_error_posonly():
    code = """
        def test_xxx(posonly1, posonly2=2, /):
            pass
    """
    assert_error(
        TFunctionsVisitor,
        code,
        TFunctionArgumentWithDefault,
        name="test_xxx",
        arg="posonly2",
    )


def test_error_arg():
    code = """
        def test_xxx(arg1, arg2=2):
            pass
    """
    assert_error(
        TFunctionsVisitor,
        code,
        TFunctionArgumentWithDefault,
        name="test_xxx",
        arg="arg2",
    )


def test_error_kwonly():
    code = """
        def test_xxx(*, kwonly1, kwonly2=2):
            pass
    """
    assert_error(
        TFunctionsVisitor,
        code,
        TFunctionArgumentWithDefault,
        name="test_xxx",
        arg="kwonly2",
    )


def test_error_multiple():
    code = """
        def test_xxx(
            posonly=1,
            /,
            arg=2,
            *,
            kwonly=3,
        ):
            pass
    """
    # flake8-plugin-utils does not allow multiple errors in a single test
    visitor = TFunctionsVisitor()
    visitor.visit(ast.parse(textwrap.dedent(code)))
    assert len(visitor.errors) == 3
    posonly_error, arg_error, kwonly_error = visitor.errors

    assert isinstance(posonly_error, TFunctionArgumentWithDefault)
    assert posonly_error.message == TFunctionArgumentWithDefault.formatted_message(
        name="test_xxx", arg="posonly"
    )
    assert posonly_error.lineno == 3

    assert isinstance(arg_error, TFunctionArgumentWithDefault)
    assert arg_error.message == TFunctionArgumentWithDefault.formatted_message(
        name="test_xxx", arg="arg"
    )
    assert arg_error.lineno == 5

    assert isinstance(kwonly_error, TFunctionArgumentWithDefault)
    assert kwonly_error.message == TFunctionArgumentWithDefault.formatted_message(
        name="test_xxx", arg="kwonly"
    )
    assert kwonly_error.lineno == 7


================================================
FILE: tests/test_PT029_warns_without_warning.py
================================================
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import WarnsWithoutException
from flake8_pytest_style.visitors import WarnsVisitor


def test_ok():
    code = """
        import pytest

        def test_something():
            with pytest.warns(UnicodeError):
                pass
    """
    assert_not_error(WarnsVisitor, code, config=DEFAULT_CONFIG)


def test_error():
    code = """
        import pytest

        def test_something():
            with pytest.warns():
                pass
    """
    assert_error(WarnsVisitor, code, WarnsWithoutException, config=DEFAULT_CONFIG)


================================================
FILE: tests/test_PT030_warns_too_broad.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import WarnsTooBroad
from flake8_pytest_style.visitors import WarnsVisitor


def test_ok():
    code = """
        import pytest
        import warnings

        def test_something():
            with pytest.warns(UserWarning, match="foo"):
                warnings.warn("foo", UserWarning)
    """
    assert_not_error(WarnsVisitor, code, config=DEFAULT_CONFIG)


def test_ok_different_error_from_config():
    code = """
        import pytest
        import warnings

        def test_something():
            with pytest.warns(BytesWarning):
                warnings.warn("foo", UserWarning)
    """
    assert_not_error(WarnsVisitor, code, config=DEFAULT_CONFIG)


@pytest.mark.parametrize("warning", ["Warning", "UserWarning"])
def test_error_no_argument_given(warning):
    code = f"""
        import pytest
        import warnings

        def test_something():
            with pytest.warns({warning}):
                warnings.warn("foo", UserWarning)
    """
    assert_error(
        WarnsVisitor,
        code,
        WarnsTooBroad,
        config=DEFAULT_CONFIG,
        warning=warning,
    )


@pytest.mark.parametrize("match", ["None", '""', 'f""'])
def test_error_match_is_empty(match):
    code = f"""
        import pytest
        import warnings

        def test_something():
            with pytest.warns(UserWarning, match={match}):
                warnings.warn("foo", UserWarning)
    """
    assert_error(
        WarnsVisitor,
        code,
        WarnsTooBroad,
        config=DEFAULT_CONFIG,
        warning="UserWarning",
    )


================================================
FILE: tests/test_PT031_warns_with_multiple_statements.py
================================================
import pytest
from flake8_plugin_utils import assert_error, assert_not_error

from flake8_pytest_style.config import DEFAULT_CONFIG
from flake8_pytest_style.errors import WarnsWithMultipleStatements
from flake8_pytest_style.visitors import WarnsVisitor


def test_ok():
    code = """
        def test_something():
            with pytest.warns(BytesWarning):
                [].size
    """
    assert_not_error(WarnsVisitor, code, config=DEFAULT_CONFIG)


@pytest.mark.parametrize("maybe_async", ["", "async "])
def test_ok_trivial_with(maybe_async):
    code = f"""
        async def test_something():
            with pytest.warns(BytesWarning):
                {maybe_async}with context_manager_under_test():
                    pass
    """
    assert_not_error(WarnsVisitor, code, config=DEFAULT_CONFIG)


def test_error_multiple_statements():
    code = """
        def test_something():
            with pytest.warns(BytesWarning):
                len([])
                [].size
    """
    assert_error(WarnsVisitor, code, WarnsWithMultipleStatements, config=DEFAULT_CONFIG)


@pytest.mark.parametrize(
    "statement", ["if", "for i in", "async for i in", "while", "with", "async with"]
)
def test_error_complex_statement(statement):
    code = f"""
        async def test_something():
            with pytest.warns(BytesWarning):
                {statement} True:
                    [].size
    """
    assert_error(WarnsVisitor, code, WarnsWithMultipleStatements, config=DEFAULT_CONFIG)


def test_error_try():
    code = """
        def test_something():
            with pytest.warns(BytesWarning):
                try:
                    [].size
                except:
                    raise
    """
    assert_error(WarnsVisitor, code, WarnsWithMultipleStatements, config=DEFAULT_CONFIG)


================================================
FILE: tests/test_config.py
================================================
from typing import List

import flake8
import pytest
from flake8.options.manager import OptionManager

from flake8_pytest_style.config import (
    DEFAULT_CONFIG,
    Config,
    ParametrizeNamesType,
    ParametrizeValuesRowType,
    ParametrizeValuesType,
)
from flake8_pytest_style.plugin import PytestStylePlugin


@pytest.fixture
def option_manager() -> OptionManager:
    manager = OptionManager(
        version=flake8.__version__,
        plugin_versions="",  # Not necessary in tests
        parents=[],
        formatter_names=[],
    )
    PytestStylePlugin.add_options(option_manager=manager)
    return manager


def parse_options(manager: OptionManager, args: List[str]) -> Config:
    namespace = manager.parse_args(args)
    return PytestStylePlugin.parse_options_to_config(
        manager,
        namespace,
        # Not sure if this is semantically correct, but this is what flake8 passes
        # to plugin's parse_options
        namespace.filenames,
    )


def test_parse_default(option_manager):
    assert parse_options(option_manager, []) == DEFAULT_CONFIG


def test_parse_raises_require_match_for(option_manager):
    config = parse_options(
        option_manager, ["--pytest-raises-require-match-for", "ValueError,TypeError"]
    )
    assert config.raises_require_match_for == ["ValueError", "TypeError"]


def test_parse_fixture_parentheses(option_manager):
    config = parse_options(option_manager, ["--pytest-fixture-no-parentheses"])
    assert config.fixture_parentheses is False


@pytest.mark.parametrize("value", list(ParametrizeNamesType))
def test_parse_parametrize_names_type(option_manager, value):
    config = parse_options(
        option_manager, ["--pytest-parametrize-names-type", value.value]
    )
    assert config.parametrize_names_type is value


@pytest.mark.parametrize("value", list(ParametrizeValuesType))
def test_parse_parametrize_values_type(option_manager, value):
    config = parse_options(
        option_manager, ["--pytest-parametrize-values-type", value.value]
    )
    assert config.parametrize_values_type is value


@pytest.mark.parametrize("value", list(ParametrizeValuesRowType))
def test_parse_parametrize_values_row_type(option_manager, value):
    config = parse_options(
        option_manager, ["--pytest-parametrize-values-row-type", value.value]
    )
    assert config.parametrize_values_row_type is value


@pytest.mark.parametrize(
    "args",
    [
        ["--pytest-parametrize-names-type", "str"],
        ["--pytest-parametrize-values-type", "str"],
        ["--pytest-parametrize-values-row-type", "str"],
    ],
)
def test_parse_invalid_enum_values(option_manager, args):
    with pytest.raises(SystemExit):  # as raised by optparse
        parse_options(option_manager, args)


================================================
FILE: tests/test_plugin.py
================================================
import importlib
import inspect
import pkgutil
from collections import Counter
from types import ModuleType
from typing import List, Set, Type, TypeVar

from flake8_pytest_style.plugin import PytestStylePlugin

T = TypeVar("T")


def _collect_subclasses(modules: List[ModuleType], base_class: Type[T]) -> Set[Type[T]]:
    result = set()
    for module in modules:
        for _, member in inspect.getmembers(module):
            if (
                inspect.isclass(member)
                and issubclass(member, base_class)
                and member is not base_class
            ):
                result.add(member)
    return result


def test_plugin_has_all_visitors():
    from flake8_plugin_utils import Visitor

    from flake8_pytest_style import visitors as visitors_package

    visitor_module_infos = pkgutil.iter_modules(
        visitors_package.__path__  # type: ignore
    )
    visitor_modules = [
        importlib.import_module(
            f"{visitors_package.__name__}.{visitor_module_info.name}"
        )
        for visitor_module_info in visitor_module_infos
    ]
    visitor_classes = _collect_subclasses(visitor_modules, Visitor)

    assert set(PytestStylePlugin.visitors) == visitor_classes


def test_all_error_codes_are_different():
    from flake8_plugin_utils import Error

    from flake8_pytest_style import errors

    error_classes = _collect_subclasses([errors], Error)
    count_by_code = Counter(error_class.code for error_class in error_classes)
    duplicate_error_codes = [code for code, count in count_by_code.items() if count > 1]
    assert not duplicate_error_codes
Download .txt
gitextract_db9ui74u/

├── .bumpversion.cfg
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug.md
│   │   └── rule-request.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── THIRD-PARTY-LICENSES
├── docs/
│   └── rules/
│       ├── PT001.md
│       ├── PT002.md
│       ├── PT003.md
│       ├── PT004.md
│       ├── PT005.md
│       ├── PT006.md
│       ├── PT007.md
│       ├── PT008.md
│       ├── PT009.md
│       ├── PT010.md
│       ├── PT011.md
│       ├── PT012.md
│       ├── PT013.md
│       ├── PT014.md
│       ├── PT015.md
│       ├── PT016.md
│       ├── PT017.md
│       ├── PT018.md
│       ├── PT019.md
│       ├── PT020.md
│       ├── PT021.md
│       ├── PT022.md
│       ├── PT023.md
│       ├── PT024.md
│       ├── PT025.md
│       ├── PT026.md
│       ├── PT027.md
│       ├── PT028.md
│       ├── PT029.md
│       ├── PT030.md
│       └── PT031.md
├── flake8_pytest_style/
│   ├── __init__.py
│   ├── config.py
│   ├── errors.py
│   ├── plugin.py
│   ├── py.typed
│   ├── utils.py
│   └── visitors/
│       ├── __init__.py
│       ├── assertion.py
│       ├── fail.py
│       ├── fixtures.py
│       ├── imports.py
│       ├── marks.py
│       ├── parametrize.py
│       ├── patch.py
│       ├── raises.py
│       ├── t_functions.py
│       ├── try_except.py
│       └── warns.py
├── pyproject.toml
├── scripts/
│   └── pypi_readme.py
├── setup.cfg
└── tests/
    ├── __init__.py
    ├── conftest.py
    ├── test_PT001_incorrect_fixture_parentheses_style.py
    ├── test_PT002_fixture_positional_args.py
    ├── test_PT003_extraneous_scope_function.py
    ├── test_PT004_missing_fixture_name_underscore.py
    ├── test_PT005_incorrect_fixture_name_underscore.py
    ├── test_PT006_parametrize_names_wrong_type.py
    ├── test_PT007_parametrize_values_wrong_type.py
    ├── test_PT008_patch_with_lambda.py
    ├── test_PT009_unittest_assertion.py
    ├── test_PT010_raises_without_exception.py
    ├── test_PT011_raises_too_broad.py
    ├── test_PT012_raises_with_multiple_statements.py
    ├── test_PT013_incorrect_pytest_import.py
    ├── test_PT014_duplicate_parametrize_test_cases.py
    ├── test_PT015_assert_always_false.py
    ├── test_PT016_fail_without_message.py
    ├── test_PT017_assert_in_except.py
    ├── test_PT018_composite_assertion.py
    ├── test_PT019_fixture_param_without_value.py
    ├── test_PT020_deprecated_yield_fixture.py
    ├── test_PT021_fixture_finalizer_callback.py
    ├── test_PT022_useless_yield_fixture.py
    ├── test_PT023_incorrect_mark_parentheses_style.py
    ├── test_PT024_unncessary_asyncio_mark_on_fixture.py
    ├── test_PT025_erroneous_usefixtures_on_fixture.py
    ├── test_PT026_usefixtures_without_parameters.py
    ├── test_PT027_unittest_raises_assertion.py
    ├── test_PT028_test_function_argument_with_default.py
    ├── test_PT029_warns_without_warning.py
    ├── test_PT030_warns_too_broad.py
    ├── test_PT031_warns_with_multiple_statements.py
    ├── test_config.py
    └── test_plugin.py
Download .txt
SYMBOL INDEX (257 symbols across 49 files)

FILE: flake8_pytest_style/config.py
  function enum_choices (line 5) | def enum_choices(enum: Type[Enum]) -> List[Any]:
  class ParametrizeNamesType (line 9) | class ParametrizeNamesType(Enum):
  class ParametrizeValuesType (line 15) | class ParametrizeValuesType(Enum):
  class ParametrizeValuesRowType (line 20) | class ParametrizeValuesRowType(Enum):
  class Config (line 25) | class Config(NamedTuple):

FILE: flake8_pytest_style/errors.py
  class IncorrectFixtureParenthesesStyle (line 4) | class IncorrectFixtureParenthesesStyle(Error):
  class FixturePositionalArgs (line 9) | class FixturePositionalArgs(Error):
  class ExtraneousScopeFunction (line 16) | class ExtraneousScopeFunction(Error):
  class MissingFixtureNameUnderscore (line 21) | class MissingFixtureNameUnderscore(Error):
  class IncorrectFixtureNameUnderscore (line 26) | class IncorrectFixtureNameUnderscore(Error):
  class ParametrizeNamesWrongType (line 31) | class ParametrizeNamesWrongType(Error):
  class ParametrizeValuesWrongType (line 36) | class ParametrizeValuesWrongType(Error):
  class PatchWithLambda (line 41) | class PatchWithLambda(Error):
  class UnittestAssertion (line 46) | class UnittestAssertion(Error):
  class RaisesWithoutException (line 51) | class RaisesWithoutException(Error):
  class RaisesTooBroad (line 56) | class RaisesTooBroad(Error):
  class RaisesWithMultipleStatements (line 64) | class RaisesWithMultipleStatements(Error):
  class IncorrectPytestImport (line 69) | class IncorrectPytestImport(Error):
  class DuplicateParametrizeTestCases (line 74) | class DuplicateParametrizeTestCases(Error):
  class AssertAlwaysFalse (line 79) | class AssertAlwaysFalse(Error):
  class FailWithoutMessage (line 84) | class FailWithoutMessage(Error):
  class AssertInExcept (line 89) | class AssertInExcept(Error):
  class CompositeAssertion (line 97) | class CompositeAssertion(Error):
  class FixtureParamWithoutValue (line 102) | class FixtureParamWithoutValue(Error):
  class DeprecatedYieldFixture (line 110) | class DeprecatedYieldFixture(Error):
  class FixtureFinalizerCallback (line 115) | class FixtureFinalizerCallback(Error):
  class UselessYieldFixture (line 120) | class UselessYieldFixture(Error):
  class IncorrectMarkParenthesesStyle (line 125) | class IncorrectMarkParenthesesStyle(Error):
  class UnnecessaryAsyncioMarkOnFixture (line 133) | class UnnecessaryAsyncioMarkOnFixture(Error):
  class ErroneousUseFixturesOnFixture (line 138) | class ErroneousUseFixturesOnFixture(Error):
  class UseFixturesWithoutParameters (line 143) | class UseFixturesWithoutParameters(Error):
  class UnittestRaisesAssertion (line 148) | class UnittestRaisesAssertion(Error):
  class TFunctionArgumentWithDefault (line 155) | class TFunctionArgumentWithDefault(Error):
  class WarnsWithoutException (line 160) | class WarnsWithoutException(Error):
  class WarnsTooBroad (line 165) | class WarnsTooBroad(Error):
  class WarnsWithMultipleStatements (line 173) | class WarnsWithMultipleStatements(Error):

FILE: flake8_pytest_style/plugin.py
  class PytestStylePlugin (line 33) | class PytestStylePlugin(Plugin[Config]):
    method add_options (line 52) | def add_options(cls, option_manager: OptionManager) -> None:
    method parse_options_to_config (line 111) | def parse_options_to_config(  # pylint: disable=unused-argument

FILE: flake8_pytest_style/utils.py
  function get_qualname (line 9) | def get_qualname(node: ast.AST) -> Optional[str]:
  class SimpleCallArgs (line 26) | class SimpleCallArgs(NamedTuple):
    method get_argument (line 30) | def get_argument(
  function get_simple_call_args (line 42) | def get_simple_call_args(node: ast.Call) -> SimpleCallArgs:
  function is_parametrize_call (line 63) | def is_parametrize_call(node: ast.Call) -> bool:
  function is_raises_call (line 68) | def is_raises_call(node: ast.Call) -> bool:
  function is_warns_call (line 73) | def is_warns_call(node: ast.Call) -> bool:
  function is_fail_call (line 78) | def is_fail_call(node: ast.Call) -> bool:
  function is_raises_with (line 83) | def is_raises_with(node: ast.With) -> bool:
  function is_warns_with (line 93) | def is_warns_with(node: ast.With) -> bool:
  class ParametrizeArgs (line 101) | class ParametrizeArgs(NamedTuple):
  function extract_parametrize_call_args (line 107) | def extract_parametrize_call_args(node: ast.Call) -> Optional[Parametriz...
  function _is_pytest_fixture (line 120) | def _is_pytest_fixture(node: ast.AST) -> bool:
  function is_pytest_yield_fixture (line 125) | def is_pytest_yield_fixture(node: ast.AST) -> bool:
  function _is_any_pytest_fixture (line 130) | def _is_any_pytest_fixture(node: ast.AST) -> bool:
  function get_fixture_decorator (line 135) | def get_fixture_decorator(node: AnyFunctionDef) -> Union[ast.Call, ast.A...
  function _is_mark (line 157) | def _is_mark(node: ast.AST) -> bool:
  function get_mark_decorators (line 165) | def get_mark_decorators(
  function get_mark_name (line 188) | def get_mark_name(node: ast.AST) -> str:
  function is_none (line 204) | def is_none(node: ast.AST) -> bool:
  function is_empty_string (line 215) | def is_empty_string(node: ast.AST) -> bool:
  function _is_empty_iterable (line 231) | def _is_empty_iterable(  # pylint:disable=too-many-return-statements
  function is_falsy_constant (line 268) | def is_falsy_constant(node: ast.AST) -> bool:
  function is_test_function (line 280) | def is_test_function(node: AnyFunctionDef) -> bool:
  function get_all_argument_names (line 285) | def get_all_argument_names(node: ast.arguments) -> List[str]:
  function walk_without_nested_functions (line 297) | def walk_without_nested_functions(root: ast.AST) -> Iterator[ast.AST]:
  function is_abstract_method (line 310) | def is_abstract_method(node: AnyFunctionDef) -> bool:
  function is_nontrivial_with_statement (line 322) | def is_nontrivial_with_statement(node: ast.AST) -> bool:

FILE: flake8_pytest_style/visitors/assertion.py
  class UnittestAssertionVisitor (line 76) | class UnittestAssertionVisitor(Visitor[Config]):
    method visit_Call (line 77) | def visit_Call(self, node: ast.Call) -> None:
  class AssertionVisitor (line 87) | class AssertionVisitor(Visitor[Config]):
    method _is_composite_condition (line 88) | def _is_composite_condition(self, expr: ast.AST) -> bool:
    method visit_Assert (line 104) | def visit_Assert(self, node: ast.Assert) -> None:

FILE: flake8_pytest_style/visitors/fail.py
  class FailVisitor (line 15) | class FailVisitor(Visitor[Config]):
    method _check_fail_call (line 16) | def _check_fail_call(self, node: ast.Call) -> None:
    method visit_Assert (line 28) | def visit_Assert(self, node: ast.Assert) -> None:
    method visit_Call (line 33) | def visit_Call(self, node: ast.Call) -> None:

FILE: flake8_pytest_style/visitors/fixtures.py
  class FixturesVisitor (line 32) | class FixturesVisitor(Visitor[Config]):
    method _check_fixture_decorator_name (line 33) | def _check_fixture_decorator_name(
    method _check_fixture_decorator (line 44) | def _check_fixture_decorator(
    method _check_fixture_returns (line 87) | def _check_fixture_returns(self, node: AnyFunctionDef) -> None:
    method _check_fixture_addfinalizer (line 121) | def _check_fixture_addfinalizer(self, node: AnyFunctionDef) -> None:
    method _check_fixture_marks (line 134) | def _check_fixture_marks(self, node: AnyFunctionDef) -> None:
    method visit_FunctionDef (line 153) | def visit_FunctionDef(self, node: AnyFunctionDef) -> None:

FILE: flake8_pytest_style/visitors/imports.py
  function _is_pytest_or_subpackage (line 9) | def _is_pytest_or_subpackage(imported_name: str) -> bool:
  class ImportsVisitor (line 13) | class ImportsVisitor(Visitor[Config]):
    method visit_Import (line 14) | def visit_Import(self, node: ast.Import) -> None:
    method visit_ImportFrom (line 23) | def visit_ImportFrom(self, node: ast.ImportFrom) -> None:

FILE: flake8_pytest_style/visitors/marks.py
  class MarksVisitor (line 18) | class MarksVisitor(Visitor[Config]):
    method _check_mark_parentheses (line 19) | def _check_mark_parentheses(
    method _check_useless_usefixtures (line 48) | def _check_useless_usefixtures(
    method visit_FunctionDef (line 62) | def visit_FunctionDef(self, node: AnyDecoratorTarget) -> None:
    method visit_ClassDef (line 70) | def visit_ClassDef(self, node: ast.ClassDef) -> None:

FILE: flake8_pytest_style/visitors/parametrize.py
  class ParametrizeVisitor (line 21) | class ParametrizeVisitor(Visitor[Config]):
    method _check_parametrize_names (line 22) | def _check_parametrize_names(
    method _get_expected_values_type_str (line 58) | def _get_expected_values_type_str(self, multiple_names: Optional[bool]...
    method _check_parametrize_values (line 66) | def _check_parametrize_values(
    method _check_parametrize_duplicates (line 103) | def _check_parametrize_duplicates(
    method _check_parametrize_call (line 118) | def _check_parametrize_call(self, node: ast.Call) -> None:
    method visit_Call (line 130) | def visit_Call(self, node: ast.Call) -> None:

FILE: flake8_pytest_style/visitors/patch.py
  class PatchVisitor (line 28) | class PatchVisitor(Visitor[Config]):
    method _check_patch_call (line 29) | def _check_patch_call(self, node: ast.Call, new_arg_number: int) -> None:
    method visit_Call (line 52) | def visit_Call(self, node: ast.Call) -> None:

FILE: flake8_pytest_style/visitors/raises.py
  class RaisesVisitor (line 22) | class RaisesVisitor(Visitor[Config]):
    method _check_raises_call (line 23) | def _check_raises_call(self, node: ast.Call) -> None:
    method _check_raises_with (line 40) | def _check_raises_with(self, node: ast.With) -> None:
    method visit_Call (line 64) | def visit_Call(self, node: ast.Call) -> None:
    method visit_With (line 68) | def visit_With(self, node: ast.With) -> None:

FILE: flake8_pytest_style/visitors/t_functions.py
  class TFunctionsVisitor (line 13) | class TFunctionsVisitor(Visitor[Config]):
    method _check_test_function_args (line 14) | def _check_test_function_args(self, node: AnyFunctionDef) -> None:
    method visit_FunctionDef (line 38) | def visit_FunctionDef(self, node: AnyFunctionDef) -> None:

FILE: flake8_pytest_style/visitors/try_except.py
  class TryExceptVisitor (line 10) | class TryExceptVisitor(Visitor[Config]):
    method __init__ (line 11) | def __init__(self, config: Optional[Config] = None) -> None:
    method visit_ExceptHandler (line 16) | def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None:
    method visit_Assert (line 25) | def visit_Assert(self, node: ast.Assert) -> None:
    method visit_Name (line 35) | def visit_Name(self, node: ast.Name) -> None:

FILE: flake8_pytest_style/visitors/warns.py
  class WarnsVisitor (line 22) | class WarnsVisitor(Visitor[Config]):
    method _check_warns_call (line 23) | def _check_warns_call(self, node: ast.Call) -> None:
    method _check_warns_with (line 40) | def _check_warns_with(self, node: ast.With) -> None:
    method visit_Call (line 64) | def visit_Call(self, node: ast.Call) -> None:
    method visit_With (line 68) | def visit_With(self, node: ast.With) -> None:

FILE: scripts/pypi_readme.py
  function process_readme (line 10) | def process_readme(readme: str, project_metadata: Container) -> str:
  function make_output_filename (line 23) | def make_output_filename(input_filename: str) -> str:
  function main (line 28) | def main() -> None:

FILE: tests/test_PT001_incorrect_fixture_parentheses_style.py
  function test_ok_no_parameters (line 12) | def test_ok_no_parameters():
  function test_ok_with_parameters (line 23) | def test_ok_with_parameters():
  function test_ok_without_parens (line 34) | def test_ok_without_parens():
  function test_error_without_parens (line 45) | def test_error_without_parens():
  function test_error_with_parens (line 63) | def test_error_with_parens():

FILE: tests/test_PT002_fixture_positional_args.py
  function test_ok_no_args (line 8) | def test_ok_no_args():
  function test_ok_only_kwargs (line 19) | def test_ok_only_kwargs():
  function test_error_only_args (line 30) | def test_error_only_args():
  function test_error_mixed (line 47) | def test_error_mixed():

FILE: tests/test_PT003_extraneous_scope_function.py
  function test_ok_no_scope (line 8) | def test_ok_no_scope():
  function test_ok_other_scope (line 19) | def test_ok_other_scope():
  function test_error (line 30) | def test_error():

FILE: tests/test_PT004_missing_fixture_name_underscore.py
  function test_ok_simple (line 8) | def test_ok_simple():
  function test_ok_with_return (line 19) | def test_ok_with_return():
  function test_ok_with_yield (line 32) | def test_ok_with_yield():
  function test_ok_abstract_with_import_abc (line 44) | def test_ok_abstract_with_import_abc():
  function test_ok_abstract_with_from_import (line 59) | def test_ok_abstract_with_from_import():
  function test_ok_ignoring_yield_from (line 74) | def test_ok_ignoring_yield_from():
  function test_error_simple (line 85) | def test_error_simple():
  function test_error_with_yield (line 102) | def test_error_with_yield():

FILE: tests/test_PT005_incorrect_fixture_name_underscore.py
  function test_ok_with_return (line 8) | def test_ok_with_return():
  function test_ok_with_yield (line 19) | def test_ok_with_yield():
  function test_ok_nested_function (line 31) | def test_ok_nested_function():
  function test_ok_abstract_with_import_abc (line 43) | def test_ok_abstract_with_import_abc():
  function test_ok_abstract_with_from_import (line 58) | def test_ok_abstract_with_from_import():
  function test_error_with_return (line 73) | def test_error_with_return():
  function test_error_with_yield (line 90) | def test_error_with_yield():
  function test_error_with_conditional_yield_from (line 108) | def test_error_with_conditional_yield_from():

FILE: tests/test_PT006_parametrize_names_wrong_type.py
  function test_ok_single (line 13) | def test_ok_single():
  function test_ok_multiple (line 33) | def test_ok_multiple(cfg_type, names):
  function test_error_single_tuple (line 49) | def test_error_single_tuple():
  function test_error_multiple (line 78) | def test_error_multiple(cfg_type, names):

FILE: tests/test_PT007_parametrize_values_wrong_type.py
  function _get_expected_type_str (line 22) | def _get_expected_type_str(values_cfg_type, rows_cfg_type):
  function test_ok_single (line 33) | def test_ok_single(values_cfg_type, values):
  function test_ok_multiple (line 71) | def test_ok_multiple(values_cfg_type, rows_cfg_type, values):
  function test_error_single (line 94) | def test_error_single(values_cfg_type, values):
  function test_error_single_tuple_as_decorator (line 113) | def test_error_single_tuple_as_decorator():
  function test_error_multiple (line 198) | def test_error_multiple(values_cfg_type, rows_cfg_type, values):
  function test_error_multiple_mixed (line 223) | def test_error_multiple_mixed(values_cfg_type, rows_cfg_type, values):

FILE: tests/test_PT008_patch_with_lambda.py
  function test_ok (line 47) | def test_ok(code_template, patch_with):
  function test_error (line 56) | def test_error(code_template, patch_with):

FILE: tests/test_PT009_unittest_assertion.py
  function test_ok_no_parameters (line 7) | def test_ok_no_parameters():
  function test_error (line 17) | def test_error():

FILE: tests/test_PT010_raises_without_exception.py
  function test_ok (line 8) | def test_ok():
  function test_error (line 19) | def test_error():

FILE: tests/test_PT011_raises_too_broad.py
  function test_ok (line 9) | def test_ok():
  function test_ok_different_error_from_config (line 20) | def test_ok_different_error_from_config():
  function test_error_no_argument_given (line 32) | def test_error_no_argument_given(exception):
  function test_error_match_is_empty (line 50) | def test_error_match_is_empty(match):

FILE: tests/test_PT012_raises_with_multiple_statements.py
  function test_ok (line 9) | def test_ok():
  function test_ok_trivial_with (line 19) | def test_ok_trivial_with(maybe_async):
  function test_error_multiple_statements (line 29) | def test_error_multiple_statements():
  function test_error_complex_statement (line 44) | def test_error_complex_statement(statement):
  function test_error_try (line 56) | def test_error_try():

FILE: tests/test_PT013_incorrect_pytest_import.py
  function test_ok (line 18) | def test_ok(code):
  function test_error (line 30) | def test_error(code):

FILE: tests/test_PT014_duplicate_parametrize_test_cases.py
  function test_error (line 8) | def test_error():

FILE: tests/test_PT015_assert_always_false.py
  function test_ok (line 8) | def test_ok():
  function test_error (line 38) | def test_error(falsy_constant):

FILE: tests/test_PT016_fail_without_message.py
  function test_ok_arg (line 8) | def test_ok_arg():
  function test_ok_kwarg (line 16) | def test_ok_kwarg():
  function test_ok_kwarg_legacy (line 24) | def test_ok_kwarg_legacy():
  function test_error (line 35) | def test_error(args):

FILE: tests/test_PT017_assert_in_except.py
  function test_ok (line 8) | def test_ok():
  function test_error (line 23) | def test_error():

FILE: tests/test_PT018_composite_assertion.py
  function test_ok (line 8) | def test_ok():
  function test_error (line 31) | def test_error(condition):

FILE: tests/test_PT019_fixture_param_without_value.py
  function test_ok_good_param_name (line 7) | def test_ok_good_param_name():
  function test_ok_non_test_function (line 15) | def test_ok_non_test_function():
  function test_error_arg (line 23) | def test_error_arg():
  function test_error_kwonly (line 31) | def test_error_kwonly():

FILE: tests/test_PT020_deprecated_yield_fixture.py
  function test_ok_no_parameters (line 12) | def test_ok_no_parameters():
  function test_ok_without_parens (line 23) | def test_ok_without_parens():
  function test_error_without_parens (line 34) | def test_error_without_parens():
  function test_error_with_parens (line 47) | def test_error_with_parens():

FILE: tests/test_PT021_fixture_finalizer_callback.py
  function test_ok_return (line 8) | def test_ok_return():
  function test_ok_yield (line 19) | def test_ok_yield():
  function test_ok_other_request (line 32) | def test_ok_other_request():
  function test_ok_other_function (line 45) | def test_ok_other_function():
  function test_ok_nested_function (line 64) | def test_ok_nested_function():
  function test_error_return (line 80) | def test_error_return():
  function test_error_yield (line 93) | def test_error_yield():

FILE: tests/test_PT022_useless_yield_fixture.py
  function test_ok_complex_logic (line 11) | def test_ok_complex_logic():
  function test_error (line 27) | def test_error():

FILE: tests/test_PT023_incorrect_mark_parentheses_style.py
  function test_ok_with_parameters_regardless_of_config (line 132) | def test_ok_with_parameters_regardless_of_config(mark_parentheses: bool):
  function test_ok_without_parens (line 145) | def test_ok_without_parens(code: str):
  function test_ok_with_parens (line 150) | def test_ok_with_parens(code: str):
  function test_error_without_parens (line 155) | def test_error_without_parens(code: str):
  function test_error_with_parens (line 168) | def test_error_with_parens(code: str):

FILE: tests/test_PT024_unncessary_asyncio_mark_on_fixture.py
  function test_ok_not_fixture (line 8) | def test_ok_not_fixture():
  function test_ok_not_fixture_no_parens (line 19) | def test_ok_not_fixture_no_parens():
  function test_error_before (line 30) | def test_error_before():
  function test_error_before_no_parens (line 44) | def test_error_before_no_parens():
  function test_error_after (line 58) | def test_error_after():
  function test_error_after_no_parens (line 72) | def test_error_after_no_parens():

FILE: tests/test_PT025_erroneous_usefixtures_on_fixture.py
  function test_ok_not_fixture (line 8) | def test_ok_not_fixture():
  function test_error_before (line 19) | def test_error_before():
  function test_error_after (line 36) | def test_error_after():

FILE: tests/test_PT026_usefixtures_without_parameters.py
  function test_ok (line 8) | def test_ok():
  function test_ok_another_mark_with_parens (line 19) | def test_ok_another_mark_with_parens():
  function test_ok_another_mark_no_parens (line 31) | def test_ok_another_mark_no_parens():
  function test_error_with_parens (line 43) | def test_error_with_parens():
  function test_error_no_parens (line 55) | def test_error_no_parens():

FILE: tests/test_PT027_unittest_raises_assertion.py
  function test_ok_no_parameters (line 7) | def test_ok_no_parameters():
  function test_error (line 18) | def test_error():

FILE: tests/test_PT028_test_function_argument_with_default.py
  function test_ok (line 10) | def test_ok():
  function test_ok_non_test_function (line 18) | def test_ok_non_test_function():
  function test_error_posonly (line 26) | def test_error_posonly():
  function test_error_arg (line 40) | def test_error_arg():
  function test_error_kwonly (line 54) | def test_error_kwonly():
  function test_error_multiple (line 68) | def test_error_multiple():

FILE: tests/test_PT029_warns_without_warning.py
  function test_ok (line 8) | def test_ok():
  function test_error (line 19) | def test_error():

FILE: tests/test_PT030_warns_too_broad.py
  function test_ok (line 9) | def test_ok():
  function test_ok_different_error_from_config (line 21) | def test_ok_different_error_from_config():
  function test_error_no_argument_given (line 34) | def test_error_no_argument_given(warning):
  function test_error_match_is_empty (line 53) | def test_error_match_is_empty(match):

FILE: tests/test_PT031_warns_with_multiple_statements.py
  function test_ok (line 9) | def test_ok():
  function test_ok_trivial_with (line 19) | def test_ok_trivial_with(maybe_async):
  function test_error_multiple_statements (line 29) | def test_error_multiple_statements():
  function test_error_complex_statement (line 42) | def test_error_complex_statement(statement):
  function test_error_try (line 52) | def test_error_try():

FILE: tests/test_config.py
  function option_manager (line 18) | def option_manager() -> OptionManager:
  function parse_options (line 29) | def parse_options(manager: OptionManager, args: List[str]) -> Config:
  function test_parse_default (line 40) | def test_parse_default(option_manager):
  function test_parse_raises_require_match_for (line 44) | def test_parse_raises_require_match_for(option_manager):
  function test_parse_fixture_parentheses (line 51) | def test_parse_fixture_parentheses(option_manager):
  function test_parse_parametrize_names_type (line 57) | def test_parse_parametrize_names_type(option_manager, value):
  function test_parse_parametrize_values_type (line 65) | def test_parse_parametrize_values_type(option_manager, value):
  function test_parse_parametrize_values_row_type (line 73) | def test_parse_parametrize_values_row_type(option_manager, value):
  function test_parse_invalid_enum_values (line 88) | def test_parse_invalid_enum_values(option_manager, args):

FILE: tests/test_plugin.py
  function _collect_subclasses (line 13) | def _collect_subclasses(modules: List[ModuleType], base_class: Type[T]) ...
  function test_plugin_has_all_visitors (line 26) | def test_plugin_has_all_visitors():
  function test_all_error_codes_are_different (line 45) | def test_all_error_codes_are_different():
Condensed preview — 98 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (165K chars).
[
  {
    "path": ".bumpversion.cfg",
    "chars": 430,
    "preview": "[bumpversion]\ncurrent_version = 2.2.0\ncommit = True\ntag = True\n\n[bumpversion:file:pyproject.toml]\nsearch = version = \"{c"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.md",
    "chars": 591,
    "preview": "---\nname: Bug\nabout: Report a bug in the plugin\ntitle: When I have {{ SOMETHING EXPECTED }} in test, {{ AN UNEXPECTED TH"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/rule-request.md",
    "chars": 379,
    "preview": "---\nname: Rule request\nabout: Suggest a new rule for the plugin\ntitle: I want a rule that will {{ DO SOMETHING AWESOME }"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 143,
    "preview": "version: 2\nupdates:\n- package-ecosystem: pip\n  directory: \"/\"\n  schedule:\n    interval: daily\n    time: \"02:00\"\n  open-p"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1136,
    "preview": "name: CI\non:\n  push:\n    branches: [master]\n    tags: ['*']\n  pull_request:\n\njobs:\n  ci:\n    runs-on: ubuntu-latest\n    "
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 946,
    "preview": "name: Release\non:\n  workflow_dispatch:\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        "
  },
  {
    "path": ".gitignore",
    "chars": 1261,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2019 Mikhail Burshteyn\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "Makefile",
    "chars": 823,
    "preview": ".PHONY: init test test-cov lint format\n\nCODE = flake8_pytest_style\nTEST = poetry run pytest --verbosity=2 --showlocals -"
  },
  {
    "path": "README.md",
    "chars": 9731,
    "preview": "# flake8-pytest-style\n\n[![pypi](https://badge.fury.io/py/flake8-pytest-style.svg)](https://pypi.org/project/flake8-pytes"
  },
  {
    "path": "THIRD-PARTY-LICENSES",
    "chars": 1328,
    "preview": "*******************************************************************************\nhttps://github.com/m-burst/cookiecutter-"
  },
  {
    "path": "docs/rules/PT001.md",
    "chars": 710,
    "preview": "# PT001\n\n`use @pytest.fixture over @pytest.fixture()`\n\n## Configuration\n\n* `pytest-fixture-no-parentheses`  \nBoolean fla"
  },
  {
    "path": "docs/rules/PT002.md",
    "chars": 411,
    "preview": "# PT002\n\n`configuration for fixture '{name}' specified via positional args, use kwargs`\n\n## Examples\n\nBad code:\n\n```pyth"
  },
  {
    "path": "docs/rules/PT003.md",
    "chars": 447,
    "preview": "# PT003\n\n`scope='function' is implied in @pytest.fixture()`\n\nFixtures with function scope should not specify scope expli"
  },
  {
    "path": "docs/rules/PT004.md",
    "chars": 1078,
    "preview": "# PT004\n\n`fixture '{name}' does not return anything, add leading underscore`\n\nThis rule does not fire on abstract fixtur"
  },
  {
    "path": "docs/rules/PT005.md",
    "chars": 929,
    "preview": "# PT005\n\n`fixture '{name}' returns a value, remove leading underscore`\n\nThis rule does not fire on abstract fixtures (th"
  },
  {
    "path": "docs/rules/PT006.md",
    "chars": 1524,
    "preview": "# PT006\n\n`wrong name(s) type in @pytest.mark.parametrize, expected {expected_type}`\n\nFor a single name the expected type"
  },
  {
    "path": "docs/rules/PT007.md",
    "chars": 1965,
    "preview": "# PT007\n\n`wrong values type in @pytest.mark.parametrize, expected {expected_type}`\n\nThe expected type of the list of row"
  },
  {
    "path": "docs/rules/PT008.md",
    "chars": 991,
    "preview": "# PT008\n\n`use return_value= instead of patching with lambda`\n\nThis checks calls to `unittest.mock.patch` from standard l"
  },
  {
    "path": "docs/rules/PT009.md",
    "chars": 457,
    "preview": "# PT009\n\n`use a regular assert instead of unittest-style '{assertion}'`\n\n## Examples\n\nBad code:\n\n```python\nimport unitte"
  },
  {
    "path": "docs/rules/PT010.md",
    "chars": 367,
    "preview": "# PT010\n\n`set the expected exception in pytest.raises()`\n\n## Examples\n\nBad code:\n\n```python\nimport pytest\n\ndef test_foo("
  },
  {
    "path": "docs/rules/PT011.md",
    "chars": 861,
    "preview": "# PT011\n\n`pytest.raises({exception}) is too broad, set the match parameter or use a more specific exception`\n\n## Configu"
  },
  {
    "path": "docs/rules/PT012.md",
    "chars": 1166,
    "preview": "# PT012\n\n`pytest.raises() block should contain a single simple statement`\n\nThis forbids multiple statements and control "
  },
  {
    "path": "docs/rules/PT013.md",
    "chars": 314,
    "preview": "# PT013\n\n`found incorrect import of pytest, use simple 'import pytest' instead`\n\n## Examples\n\nBad code:\n\n```python\nimpor"
  },
  {
    "path": "docs/rules/PT014.md",
    "chars": 342,
    "preview": "# PT014\n\n`found duplicate test cases {indexes} in @pytest.mark.parametrize`\n\n## Examples\n\nBad code:\n\n```python\nimport py"
  },
  {
    "path": "docs/rules/PT015.md",
    "chars": 444,
    "preview": "# PT015\n\n`assertion always fails, replace with pytest.fail()`\n\n## Examples\n\nBad code:\n\n```python\ndef test_foo():\n    if "
  },
  {
    "path": "docs/rules/PT016.md",
    "chars": 786,
    "preview": "# PT016\n\n`no message passed to pytest.fail()`\n\nThe function `pytest.fail` must be called either with a positional argume"
  },
  {
    "path": "docs/rules/PT017.md",
    "chars": 477,
    "preview": "# PT017\n\n`found assertion on exception {name} in except block, use pytest.raises() instead`\n\n## Examples\n\nBad code:\n\n```"
  },
  {
    "path": "docs/rules/PT018.md",
    "chars": 546,
    "preview": "# PT018\n\n`assertion should be broken down into multiple parts`\n\nThis violation is reported when the plugin encounter an "
  },
  {
    "path": "docs/rules/PT019.md",
    "chars": 354,
    "preview": "# PT019\n\n`fixture {name} without value is injected as parameter, use @pytest.mark.usefixtures instead`\n\n## Examples\n\nBad"
  },
  {
    "path": "docs/rules/PT020.md",
    "chars": 406,
    "preview": "# PT020\n\n`@pytest.yield_fixture is deprecated, use @pytest.fixture`\n\n## Examples\n\nBad code:\n\n```python\nimport pytest\n\n@p"
  },
  {
    "path": "docs/rules/PT021.md",
    "chars": 1095,
    "preview": "# PT021\n\n`use yield instead of request.addfinalizer`\n\n`pytest` offers two ways to perform cleanup in fixture code.  One "
  },
  {
    "path": "docs/rules/PT022.md",
    "chars": 673,
    "preview": "# PT022\n\n`no teardown in fixture {name}, use return instead of yield`\n\n`yield` in fixtures is only useful and semantical"
  },
  {
    "path": "docs/rules/PT023.md",
    "chars": 718,
    "preview": "# PT023\n\n`use @pytest.mark.foo over @pytest.mark.foo()`\n\n## Configuration\n\n* `pytest-mark-no-parentheses`  \nBoolean flag"
  },
  {
    "path": "docs/rules/PT024.md",
    "chars": 363,
    "preview": "# PT024\n\n`pytest.mark.asyncio is unnecessary for fixtures`\n\n## Examples\n\nBad code:\n\n```python\nimport pytest\n\n@pytest.mar"
  },
  {
    "path": "docs/rules/PT025.md",
    "chars": 708,
    "preview": "# PT025\n\n`pytest.mark.usefixtures has no effect on fixtures`\n\n## Examples\n\nBad code:\n\n```python\nimport pytest\n\n@pytest.f"
  },
  {
    "path": "docs/rules/PT026.md",
    "chars": 396,
    "preview": "# PT023\n\n`useless pytest.mark.usefixtures without parameters`\n\n## Examples\n\nBad code:\n\n```python\nimport pytest\n\n@pytest."
  },
  {
    "path": "docs/rules/PT027.md",
    "chars": 527,
    "preview": "# PT027\n\n`use pytest.raises() instead of unittest-style '{assertion}'`\n\n## Examples\n\nBad code:\n\n```python\nimport unittes"
  },
  {
    "path": "docs/rules/PT028.md",
    "chars": 432,
    "preview": "# PT028\n\n`test function {name} has default value for argument {arg}, remove it`\n\n## Examples\n\nBad code:\n\n```python\ndef t"
  },
  {
    "path": "docs/rules/PT029.md",
    "chars": 358,
    "preview": "# PT029\n\n`set the expected warning in pytest.warns()`\n\n## Examples\n\nBad code:\n\n```python\nimport pytest\n\ndef test_foo():\n"
  },
  {
    "path": "docs/rules/PT030.md",
    "chars": 797,
    "preview": "# PT030\n\n`pytest.warns({warning}) is too broad, set the match parameter or use a more specific warning`\n\n## Configuratio"
  },
  {
    "path": "docs/rules/PT031.md",
    "chars": 1143,
    "preview": "# PT031\n\n`pytest.warns() block should contain a single simple statement`\n\nThis forbids multiple statements and control f"
  },
  {
    "path": "flake8_pytest_style/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "flake8_pytest_style/config.py",
    "chars": 1267,
    "preview": "from enum import Enum\nfrom typing import Any, List, NamedTuple, Type\n\n\ndef enum_choices(enum: Type[Enum]) -> List[Any]:\n"
  },
  {
    "path": "flake8_pytest_style/errors.py",
    "chars": 4614,
    "preview": "from flake8_plugin_utils import Error\n\n\nclass IncorrectFixtureParenthesesStyle(Error):\n    code = \"PT001\"\n    message = "
  },
  {
    "path": "flake8_pytest_style/plugin.py",
    "chars": 4620,
    "preview": "import argparse\nfrom typing import List\n\nfrom flake8.options.manager import OptionManager\nfrom flake8_plugin_utils impor"
  },
  {
    "path": "flake8_pytest_style/py.typed",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "flake8_pytest_style/utils.py",
    "chars": 9986,
    "preview": "import ast\nfrom collections import deque\nfrom typing import Dict, Iterator, List, NamedTuple, Optional, Tuple, Union\n\nAn"
  },
  {
    "path": "flake8_pytest_style/visitors/__init__.py",
    "chars": 719,
    "preview": "from .assertion import AssertionVisitor, UnittestAssertionVisitor\nfrom .fail import FailVisitor\nfrom .fixtures import Fi"
  },
  {
    "path": "flake8_pytest_style/visitors/assertion.py",
    "chars": 3015,
    "preview": "import ast\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_st"
  },
  {
    "path": "flake8_pytest_style/visitors/fail.py",
    "chars": 1360,
    "preview": "import ast\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_st"
  },
  {
    "path": "flake8_pytest_style/visitors/fixtures.py",
    "chars": 6128,
    "preview": "import ast\nfrom typing import Set, Type, Union\n\nfrom flake8_plugin_utils import Error, Visitor\n\nfrom flake8_pytest_style"
  },
  {
    "path": "flake8_pytest_style/visitors/imports.py",
    "chars": 914,
    "preview": "import ast\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_st"
  },
  {
    "path": "flake8_pytest_style/visitors/marks.py",
    "chars": 2326,
    "preview": "import ast\nfrom typing import Union\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Con"
  },
  {
    "path": "flake8_pytest_style/visitors/parametrize.py",
    "chars": 4813,
    "preview": "import ast\nimport itertools\nfrom typing import Optional\n\nfrom flake8_plugin_utils import Visitor, check_equivalent_nodes"
  },
  {
    "path": "flake8_pytest_style/visitors/patch.py",
    "chars": 1781,
    "preview": "import ast\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_st"
  },
  {
    "path": "flake8_pytest_style/visitors/raises.py",
    "chars": 2115,
    "preview": "import ast\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_st"
  },
  {
    "path": "flake8_pytest_style/visitors/t_functions.py",
    "chars": 1941,
    "preview": "from flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_style.errors i"
  },
  {
    "path": "flake8_pytest_style/visitors/try_except.py",
    "chars": 1191,
    "preview": "import ast\nfrom typing import List, Optional\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config i"
  },
  {
    "path": "flake8_pytest_style/visitors/warns.py",
    "chars": 2082,
    "preview": "import ast\n\nfrom flake8_plugin_utils import Visitor\n\nfrom flake8_pytest_style.config import Config\nfrom flake8_pytest_st"
  },
  {
    "path": "pyproject.toml",
    "chars": 2380,
    "preview": "[project]\nname = \"flake8-pytest-style\"\nversion = \"2.2.0\"\ndescription = \"A flake8 plugin checking common style issues or "
  },
  {
    "path": "scripts/pypi_readme.py",
    "chars": 1565,
    "preview": "#!/usr/bin/env python3\nimport pathlib\nimport re\nfrom typing import Match, cast\n\nimport tomlkit\nfrom tomlkit.container im"
  },
  {
    "path": "setup.cfg",
    "chars": 1650,
    "preview": "[flake8]\nenable-extensions = G\nexclude = .git, .venv\nextend-ignore =\n    ; 'id' is a python builtin, consider renaming t"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/conftest.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/test_PT001_incorrect_fixture_parentheses_style.py",
    "chars": 1833,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT002_fixture_positional_args.py",
    "chars": 1302,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT003_extraneous_scope_function.py",
    "chars": 920,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT004_missing_fixture_name_underscore.py",
    "chars": 2621,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT005_incorrect_fixture_name_underscore.py",
    "chars": 2983,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT006_parametrize_names_wrong_type.py",
    "chars": 2363,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEF"
  },
  {
    "path": "tests/test_PT007_parametrize_values_wrong_type.py",
    "chars": 6509,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import (\n "
  },
  {
    "path": "tests/test_PT008_patch_with_lambda.py",
    "chars": 1566,
    "preview": "import sys\n\nimport pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.error"
  },
  {
    "path": "tests/test_PT009_unittest_assertion.py",
    "chars": 599,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors import UnittestAssertion"
  },
  {
    "path": "tests/test_PT010_raises_without_exception.py",
    "chars": 696,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT011_raises_too_broad.py",
    "chars": 1699,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEF"
  },
  {
    "path": "tests/test_PT012_raises_with_multiple_statements.py",
    "chars": 1879,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEF"
  },
  {
    "path": "tests/test_PT013_incorrect_pytest_import.py",
    "chars": 761,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors import Inc"
  },
  {
    "path": "tests/test_PT014_duplicate_parametrize_test_cases.py",
    "chars": 667,
    "preview": "from flake8_plugin_utils import assert_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfrom flake8_pytest_s"
  },
  {
    "path": "tests/test_PT015_assert_always_false.py",
    "chars": 847,
    "preview": "import pytest\nfrom flake8_plugin_utils.utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors impo"
  },
  {
    "path": "tests/test_PT016_fail_without_message.py",
    "chars": 942,
    "preview": "import pytest\nfrom flake8_plugin_utils.utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors impo"
  },
  {
    "path": "tests/test_PT017_assert_in_except.py",
    "chars": 892,
    "preview": "from flake8_plugin_utils.utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CON"
  },
  {
    "path": "tests/test_PT018_composite_assertion.py",
    "chars": 1122,
    "preview": "import pytest\nfrom flake8_plugin_utils.utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors impo"
  },
  {
    "path": "tests/test_PT019_fixture_param_without_value.py",
    "chars": 857,
    "preview": "from flake8_plugin_utils.utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors import FixturePara"
  },
  {
    "path": "tests/test_PT020_deprecated_yield_fixture.py",
    "chars": 1415,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT021_fixture_finalizer_callback.py",
    "chars": 2844,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT022_useless_yield_fixture.py",
    "chars": 1041,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT023_incorrect_mark_parentheses_style.py",
    "chars": 4232,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEF"
  },
  {
    "path": "tests/test_PT024_unncessary_asyncio_mark_on_fixture.py",
    "chars": 1884,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT025_erroneous_usefixtures_on_fixture.py",
    "chars": 1100,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT026_usefixtures_without_parameters.py",
    "chars": 1606,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT027_unittest_raises_assertion.py",
    "chars": 738,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.errors import UnittestRaisesAss"
  },
  {
    "path": "tests/test_PT028_test_function_argument_with_default.py",
    "chars": 2481,
    "preview": "import ast\nimport textwrap\n\nfrom flake8_plugin_utils.utils import assert_error, assert_not_error\n\nfrom flake8_pytest_sty"
  },
  {
    "path": "tests/test_PT029_warns_without_warning.py",
    "chars": 689,
    "preview": "from flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEFAULT_CONFIG\nfr"
  },
  {
    "path": "tests/test_PT030_warns_too_broad.py",
    "chars": 1724,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEF"
  },
  {
    "path": "tests/test_PT031_warns_with_multiple_statements.py",
    "chars": 1812,
    "preview": "import pytest\nfrom flake8_plugin_utils import assert_error, assert_not_error\n\nfrom flake8_pytest_style.config import DEF"
  },
  {
    "path": "tests/test_config.py",
    "chars": 2773,
    "preview": "from typing import List\n\nimport flake8\nimport pytest\nfrom flake8.options.manager import OptionManager\n\nfrom flake8_pytes"
  },
  {
    "path": "tests/test_plugin.py",
    "chars": 1616,
    "preview": "import importlib\nimport inspect\nimport pkgutil\nfrom collections import Counter\nfrom types import ModuleType\nfrom typing "
  }
]

About this extraction

This page contains the full source code of the m-burst/flake8-pytest-style GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 98 files (147.6 KB), approximately 37.6k tokens, and a symbol index with 257 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!