Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-burst/flake8-pytest-style
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.6.0
Choose a base ref
...
head repository: m-burst/flake8-pytest-style
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.7.0
Choose a head ref
Loading
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.6.0
current_version = 1.7.0
commit = True
tag = True

2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug.md
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ assignees: ''
## What's wrong

<!--
Please describe what is not working. Perhaps the plugin reports a violation when it shoudn't, or vice versa?
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.
-->

6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -11,11 +11,11 @@ jobs:
strategy:
matrix:
python-version:
- 3.6
- 3.7
- 3.8
- 3.9
- "3.10"
- 3.11

steps:
- uses: actions/checkout@v2
@@ -24,9 +24,9 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- uses: snok/install-poetry@v1.2.1
- uses: snok/install-poetry@v1
with:
version: 1.1.11
version: 1.3.1

- name: Show environment info
run: |
6 changes: 4 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
python-version:
- 3.9
- "3.10"

steps:
- uses: actions/checkout@v2
@@ -23,7 +23,9 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

- uses: Gr1N/setup-poetry@v4
- uses: snok/install-poetry@v1
with:
version: 1.1.12

- name: Setup dependencies cache
uses: actions/cache@v2
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# flake8-pytest-style

[![pypi](https://badge.fury.io/py/flake8-pytest-style.svg)](https://pypi.org/project/flake8-pytest-style)
[![Python: 3.6+](https://img.shields.io/badge/Python-3.6+-blue.svg)](https://pypi.org/project/flake8-pytest-style)
[![Python: 3.7+](https://img.shields.io/badge/Python-3.7+-blue.svg)](https://pypi.org/project/flake8-pytest-style)
[![Downloads](https://img.shields.io/pypi/dm/flake8-pytest-style.svg)](https://pypistats.org/packages/flake8-pytest-style)
[![Build Status](https://github.com/m-burst/flake8-pytest-style/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/m-burst/flake8-pytest-style/actions/workflows/ci.yml)
[![Code coverage](https://codecov.io/gh/m-burst/flake8-pytest-style/branch/master/graph/badge.svg)](https://codecov.io/gh/m-burst/flake8-pytest-style)
@@ -42,6 +42,7 @@ Currently the following errors are reported:
| [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}' |

## Installation

@@ -84,6 +85,12 @@ MIT

...

**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
@@ -229,3 +236,4 @@ MIT
[PT024]: /docs/rules/PT024.md
[PT025]: /docs/rules/PT025.md
[PT026]: /docs/rules/PT026.md
[PT027]: /docs/rules/PT027.md
32 changes: 32 additions & 0 deletions docs/rules/PT027.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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
5 changes: 5 additions & 0 deletions flake8_pytest_style/errors.py
Original file line number Diff line number Diff line change
@@ -143,3 +143,8 @@ class ErroneousUseFixturesOnFixture(Error):
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}'"
2 changes: 1 addition & 1 deletion flake8_pytest_style/plugin.py
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
UnittestAssertionVisitor,
)

__version__ = '1.6.0'
__version__ = '1.7.0'


class PytestStylePlugin(Plugin[Config]):
27 changes: 18 additions & 9 deletions flake8_pytest_style/visitors/assertion.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,11 @@
from flake8_plugin_utils import Visitor

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

_UNITTEST_ASSERT_NAMES = (
'assertAlmostEqual',
@@ -32,23 +36,28 @@
'assertNotIn',
'assertNotIsInstance',
'assertNotRegexpMatches',
'assertRaises',
'assertRaisesMessage',
'assertRaisesRegexp',
'assertRegexpMatches',
'assertSetEqual',
'assertTrue',
'assert_',
)

_UNITTEST_ASSERT_RAISES_NAMES = (
'assertRaises',
'assertRaisesMessage',
'assertRaisesRegexp',
)


class UnittestAssertionVisitor(Visitor[Config]):
def visit_Call(self, node: ast.Call) -> None:
if (
isinstance(node.func, ast.Attribute)
and node.func.attr in _UNITTEST_ASSERT_NAMES
):
self.error_from_node(UnittestAssertion, node, assertion=node.func.attr)
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]):
Loading