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
[](https://pypi.org/project/flake8-pytest-style)
[](https://pypi.org/project/flake8-pytest-style)
[](https://pypistats.org/packages/flake8-pytest-style)
[](https://github.com/m-burst/flake8-pytest-style/actions/workflows/ci.yml)
[](https://codecov.io/gh/m-burst/flake8-pytest-style)
[](https://en.wikipedia.org/wiki/MIT_License)
[](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` — see [PT001]
* `pytest-parametrize-names-type` — see [PT006]
* `pytest-parametrize-values-type` — see [PT007]
* `pytest-parametrize-values-row-type` — see [PT007]
* `pytest-raises-require-match-for` — see [PT011]
* `pytest-mark-no-parentheses` — see [PT023]
* `pytest-warns-require-match-for` — 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` — a comma-separated list, e.g. `@pytest.mark.parametrize('name1,name2', ...)`
* `tuple` (default) — e.g. `@pytest.mark.parametrize(('name1', 'name2'), ...)`
* `list` — 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` — e.g. `@pytest.mark.parametrize('name', (1, 2, 3))`
* `list` (default) — 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) — e.g. `@pytest.mark.parametrize(('name1', 'name2'), [(1, 2), (3, 4)])`
* `list` — 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
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
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[](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.