Commit Graph

1388 Commits

Author SHA1 Message Date
Dan Parizher 029de784f1
[`flake8-use-pathlib`] Fix false negative on direct `Path()` instantiation (`PTH210`) (#19388)
## Summary

Fixes #19329

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-07-16 20:43:03 +00:00
frank ff94fe7447
Treat form feed as valid whitespace before a semicolon (#19343)
fixes #19310
2025-07-16 16:39:05 -04:00
Dan Parizher b2501b45e0
[`pylint`] Detect indirect `pathlib.Path` usages for `unspecified-encoding` (PLW1514) (#19304)
## Summary

Fixes #19294

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-07-16 12:57:31 -04:00
Dan Parizher 291699b375
[`refurb`] `FURB164` fix should validate arguments and should usually be marked unsafe (#19136)
## Summary

Fixes #19076

An attempt at fixing #19076 where the rule could change program behavior
by incorrectly converting from_float/from_decimal method calls to
constructor calls.

The fix implements argument validation using Ruff's existing type
inference system (`ResolvedPythonType`, `typing::is_int`,
`typing::is_float`) to determine when conversions are actually safe,
adds logic to detect invalid method calls (wrong argument counts,
incorrect keyword names) and suppress fixes for them, and changes the
default fix applicability from `Safe` to `Unsafe` with safe fixes only
offered when the argument type is known to be compatible and no
problematic keywords are used.

One uncertainty is whether the type inference catches all possible edge
cases in complex codebases, but the new approach is significantly more
conservative and safer than the previous implementation.

## Test Plan

I updated the existing test fixtures with edge cases from the issue and
manually verified behavior with temporary test files for
valid/unsafe/invalid scenarios.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-07-16 15:38:33 +00:00
Brent Westbrook 893f5727e5
[`flake8-type-checking`, `pyupgrade`, `ruff`] Add `from __future__ import annotations` when it would allow new fixes (`TC001`, `TC002`, `TC003`, `UP037`, `RUF013`) (#19100)
## Summary

This is a second attempt at addressing
https://github.com/astral-sh/ruff/issues/18502 instead of reusing
`FA100` (#18919).

This PR:
- adds a new `lint.allow-importing-future-annotations` option
- uses the option to add a `__future__` import when it would trigger
`TC001`, `TC002`, or `TC003`
- uses the option to add an import when it would allow unquoting more
annotations in [quoted-annotation
(UP037)](https://docs.astral.sh/ruff/rules/quoted-annotation/#quoted-annotation-up037)
- uses the option to allow the `|` union syntax before 3.10 in
[implicit-optional
(RUF013)](https://docs.astral.sh/ruff/rules/implicit-optional/#implicit-optional-ruf013)

I started adding a fix for [runtime-string-union
(TC010)](https://docs.astral.sh/ruff/rules/runtime-string-union/#runtime-string-union-tc010)
too, as mentioned in my previous
[comment](https://github.com/astral-sh/ruff/issues/18502#issuecomment-3005238092),
but some of the existing tests already imported `from __future__ import
annotations`, so I think we intentionally flag these cases for the user
to inspect. Adding the import is _a_ fix but probably not the best one.

## Test Plan

Existing `TC` tests, new copies of them with the option enabled, and new
tests based on ideas in
https://github.com/astral-sh/ruff/pull/18919#discussion_r2166292705 and
the following thread. For UP037 and RUF013, the new tests are also
copies of the existing tests, with the new option enabled. The easiest
way to review them is probably by their diffs from the existing
snapshots:

### UP037

`UP037_0.py` and `UP037_2.pyi` have no diffs. The diff for `UP037_1.py`
is below. It correctly unquotes an annotation in module scope that would
otherwise be invalid.

<details><summary>UP037_1.py</summary>

```diff
3d2
< snapshot_kind: text
23c22,42
< 12 12 |
---
> 12 12 |
>
> UP037_1.py:14:4: UP037 [*] Remove quotes from type annotation
>    |
> 13 | # OK
> 14 | X: "Tuple[int, int]" = (0, 0)
>    |    ^^^^^^^^^^^^^^^^^ UP037
>    |
>    = help: Remove quotes
>
> ℹ Unsafe fix
>    1  |+from __future__ import annotations
> 1  2  | from typing import TYPE_CHECKING
> 2  3  |
> 3  4  | if TYPE_CHECKING:
> --------------------------------------------------------------------------------
> 11 12 |
> 12 13 |
> 13 14 | # OK
> 14    |-X: "Tuple[int, int]" = (0, 0)
>    15 |+X: Tuple[int, int] = (0, 0)
```

</details>

### RUF013

The diffs here are mostly just the imports because the original snaps
were on 3.13. So we're getting the same fixes now on 3.9.

<details><summary>RUF013_0.py</summary>

```diff
3d2
< snapshot_kind: text
14,16c13,20
< 17 17 |     pass
< 18 18 | 
< 19 19 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 17 18 |     pass
> 18 19 | 
> 19 20 | 
18,21c22,25
<    20 |+def f(arg: int | None = None):  # RUF013
< 21 21 |     pass
< 22 22 | 
< 23 23 | 
---
>    21 |+def f(arg: int | None = None):  # RUF013
> 21 22 |     pass
> 22 23 | 
> 23 24 | 
32,34c36,43
< 21 21 |     pass
< 22 22 | 
< 23 23 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 21 22 |     pass
> 22 23 | 
> 23 24 | 
36,39c45,48
<    24 |+def f(arg: str | None = None):  # RUF013
< 25 25 |     pass
< 26 26 | 
< 27 27 | 
---
>    25 |+def f(arg: str | None = None):  # RUF013
> 25 26 |     pass
> 26 27 | 
> 27 28 | 
50,52c59,66
< 25 25 |     pass
< 26 26 | 
< 27 27 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 25 26 |     pass
> 26 27 | 
> 27 28 | 
54,57c68,71
<    28 |+def f(arg: Tuple[str] | None = None):  # RUF013
< 29 29 |     pass
< 30 30 | 
< 31 31 | 
---
>    29 |+def f(arg: Tuple[str] | None = None):  # RUF013
> 29 30 |     pass
> 30 31 | 
> 31 32 | 
68,70c82,89
< 55 55 |     pass
< 56 56 | 
< 57 57 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 55 56 |     pass
> 56 57 | 
> 57 58 | 
72,75c91,94
<    58 |+def f(arg: Union | None = None):  # RUF013
< 59 59 |     pass
< 60 60 | 
< 61 61 | 
---
>    59 |+def f(arg: Union | None = None):  # RUF013
> 59 60 |     pass
> 60 61 | 
> 61 62 | 
86,88c105,112
< 59 59 |     pass
< 60 60 | 
< 61 61 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 59 60 |     pass
> 60 61 | 
> 61 62 | 
90,93c114,117
<    62 |+def f(arg: Union[int] | None = None):  # RUF013
< 63 63 |     pass
< 64 64 | 
< 65 65 | 
---
>    63 |+def f(arg: Union[int] | None = None):  # RUF013
> 63 64 |     pass
> 64 65 | 
> 65 66 | 
104,106c128,135
< 63 63 |     pass
< 64 64 | 
< 65 65 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 63 64 |     pass
> 64 65 | 
> 65 66 | 
108,111c137,140
<    66 |+def f(arg: Union[int, str] | None = None):  # RUF013
< 67 67 |     pass
< 68 68 | 
< 69 69 | 
---
>    67 |+def f(arg: Union[int, str] | None = None):  # RUF013
> 67 68 |     pass
> 68 69 | 
> 69 70 | 
122,124c151,158
< 82 82 |     pass
< 83 83 | 
< 84 84 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 82 83 |     pass
> 83 84 | 
> 84 85 | 
126,129c160,163
<    85 |+def f(arg: int | float | None = None):  # RUF013
< 86 86 |     pass
< 87 87 | 
< 88 88 | 
---
>    86 |+def f(arg: int | float | None = None):  # RUF013
> 86 87 |     pass
> 87 88 | 
> 88 89 | 
140,142c174,181
< 86 86 |     pass
< 87 87 | 
< 88 88 | 
---
>    1  |+from __future__ import annotations
> 1  2  | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 86 87 |     pass
> 87 88 | 
> 88 89 | 
144,147c183,186
<    89 |+def f(arg: int | float | str | bytes | None = None):  # RUF013
< 90 90 |     pass
< 91 91 | 
< 92 92 | 
---
>    90 |+def f(arg: int | float | str | bytes | None = None):  # RUF013
> 90 91 |     pass
> 91 92 | 
> 92 93 | 
158,160c197,204
< 105 105 |     pass
< 106 106 | 
< 107 107 | 
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 105 106 |     pass
> 106 107 | 
> 107 108 | 
162,165c206,209
<     108 |+def f(arg: Literal[1] | None = None):  # RUF013
< 109 109 |     pass
< 110 110 | 
< 111 111 | 
---
>     109 |+def f(arg: Literal[1] | None = None):  # RUF013
> 109 110 |     pass
> 110 111 | 
> 111 112 | 
176,178c220,227
< 109 109 |     pass
< 110 110 | 
< 111 111 | 
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 109 110 |     pass
> 110 111 | 
> 111 112 | 
180,183c229,232
<     112 |+def f(arg: Literal[1, "foo"] | None = None):  # RUF013
< 113 113 |     pass
< 114 114 | 
< 115 115 | 
---
>     113 |+def f(arg: Literal[1, "foo"] | None = None):  # RUF013
> 113 114 |     pass
> 114 115 | 
> 115 116 | 
194,196c243,250
< 128 128 |     pass
< 129 129 | 
< 130 130 | 
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 128 129 |     pass
> 129 130 | 
> 130 131 | 
198,201c252,255
<     131 |+def f(arg: Annotated[int | None, ...] = None):  # RUF013
< 132 132 |     pass
< 133 133 | 
< 134 134 | 
---
>     132 |+def f(arg: Annotated[int | None, ...] = None):  # RUF013
> 132 133 |     pass
> 133 134 | 
> 134 135 | 
212,214c266,273
< 132 132 |     pass
< 133 133 | 
< 134 134 | 
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 132 133 |     pass
> 133 134 | 
> 134 135 | 
216,219c275,278
<     135 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None):  # RUF013
< 136 136 |     pass
< 137 137 | 
< 138 138 | 
---
>     136 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None):  # RUF013
> 136 137 |     pass
> 137 138 | 
> 138 139 | 
232,234c291,298
< 148 148 | 
< 149 149 | 
< 150 150 | def f(
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 148 149 | 
> 149 150 | 
> 150 151 | def f(
236,239c300,303
<     151 |+    arg1: int | None = None,  # RUF013
< 152 152 |     arg2: Union[int, float] = None,  # RUF013
< 153 153 |     arg3: Literal[1, 2, 3] = None,  # RUF013
< 154 154 | ):
---
>     152 |+    arg1: int | None = None,  # RUF013
> 152 153 |     arg2: Union[int, float] = None,  # RUF013
> 153 154 |     arg3: Literal[1, 2, 3] = None,  # RUF013
> 154 155 | ):
253,255c317,324
< 149 149 | 
< 150 150 | def f(
< 151 151 |     arg1: int = None,  # RUF013
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 149 150 | 
> 150 151 | def f(
> 151 152 |     arg1: int = None,  # RUF013
257,260c326,329
<     152 |+    arg2: Union[int, float] | None = None,  # RUF013
< 153 153 |     arg3: Literal[1, 2, 3] = None,  # RUF013
< 154 154 | ):
< 155 155 |     pass
---
>     153 |+    arg2: Union[int, float] | None = None,  # RUF013
> 153 154 |     arg3: Literal[1, 2, 3] = None,  # RUF013
> 154 155 | ):
> 155 156 |     pass
274,276c343,350
< 150 150 | def f(
< 151 151 |     arg1: int = None,  # RUF013
< 152 152 |     arg2: Union[int, float] = None,  # RUF013
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 150 151 | def f(
> 151 152 |     arg1: int = None,  # RUF013
> 152 153 |     arg2: Union[int, float] = None,  # RUF013
278,281c352,355
<     153 |+    arg3: Literal[1, 2, 3] | None = None,  # RUF013
< 154 154 | ):
< 155 155 |     pass
< 156 156 | 
---
>     154 |+    arg3: Literal[1, 2, 3] | None = None,  # RUF013
> 154 155 | ):
> 155 156 |     pass
> 156 157 | 
292,294c366,373
< 178 178 |     pass
< 179 179 | 
< 180 180 | 
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 178 179 |     pass
> 179 180 | 
> 180 181 | 
296,299c375,378
<     181 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None):  # RUF013
< 182 182 |     pass
< 183 183 | 
< 184 184 | 
---
>     182 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None):  # RUF013
> 182 183 |     pass
> 183 184 | 
> 184 185 | 
307c386
<     = help: Convert to `T | None`
---
>     = help: Convert to `Optional[T]`
314c393
<     188 |+def f(arg: "int | None" = None):  # RUF013
---
>     188 |+def f(arg: "Optional[int]" = None):  # RUF013
325c404
<     = help: Convert to `T | None`
---
>     = help: Convert to `Optional[T]`
332c411
<     192 |+def f(arg: "str | None" = None):  # RUF013
---
>     192 |+def f(arg: "Optional[str]" = None):  # RUF013
343c422
<     = help: Convert to `T | None`
---
>     = help: Convert to `Optional[T]`
354,356c433,440
< 201 201 |     pass
< 202 202 | 
< 203 203 | 
---
>     1   |+from __future__ import annotations
> 1   2   | from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
> 2   3   | 
> 3   4   | 
> --------------------------------------------------------------------------------
> 201 202 |     pass
> 202 203 | 
> 203 204 | 
358,361c442,445
<     204 |+def f(arg: Union["int", "str"] | None = None):  # RUF013
< 205 205 |     pass
< 206 206 | 
< 207 207 |
---
>     205 |+def f(arg: Union["int", "str"] | None = None):  # RUF013
> 205 206 |     pass
> 206 207 | 
> 207 208 |
```

</details>

<details><summary>RUF013_1.py</summary>

```diff
3d2
< snapshot_kind: text
15,16c14,16
< 2 2 |
< 3 3 |
---
>   2 |+from __future__ import annotations
> 2 3 |
> 3 4 |
18,19c18,19
<   4 |+def f(arg: int | None = None):  # RUF013
< 5 5 |     pass
---
>   5 |+def f(arg: int | None = None):  # RUF013
> 5 6 |     pass
```

</details>

<details><summary>RUF013_3.py</summary>

```diff
3d2
< snapshot_kind: text
14,16c13,16
< 1 1 | import typing
< 2 2 | 
< 3 3 | 
---
>   1 |+from __future__ import annotations
> 1 2 | import typing
> 2 3 | 
> 3 4 | 
18,21c18,21
<   4 |+def f(arg: typing.List[str] | None = None):  # RUF013
< 5 5 |     pass
< 6 6 | 
< 7 7 | 
---
>   5 |+def f(arg: typing.List[str] | None = None):  # RUF013
> 5 6 |     pass
> 6 7 | 
> 7 8 | 
32,34c32,39
< 19 19 |     pass
< 20 20 | 
< 21 21 | 
---
>    1  |+from __future__ import annotations
> 1  2  | import typing
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 19 20 |     pass
> 20 21 | 
> 21 22 | 
36,39c41,44
<    22 |+def f(arg: typing.Union[int, str] | None = None):  # RUF013
< 23 23 |     pass
< 24 24 | 
< 25 25 | 
---
>    23 |+def f(arg: typing.Union[int, str] | None = None):  # RUF013
> 23 24 |     pass
> 24 25 | 
> 25 26 | 
50,52c55,62
< 26 26 | # Literal
< 27 27 | 
< 28 28 | 
---
>    1  |+from __future__ import annotations
> 1  2  | import typing
> 2  3  | 
> 3  4  | 
> --------------------------------------------------------------------------------
> 26 27 | # Literal
> 27 28 | 
> 28 29 | 
54,55c64,65
<    29 |+def f(arg: typing.Literal[1, "foo", True] | None = None):  # RUF013
< 30 30 |     pass
---
>    30 |+def f(arg: typing.Literal[1, "foo", True] | None = None):  # RUF013
> 30 31 |     pass
```

</details>

<details><summary>RUF013_4.py</summary>

```diff
3d2
< snapshot_kind: text
13,15c12,20
< 12 12 | def multiple_1(arg1: Optional, arg2: Optional = None): ...
< 13 13 |
< 14 14 |
---
> 1  1  | # https://github.com/astral-sh/ruff/issues/13833
>    2  |+from __future__ import annotations
> 2  3  |
> 3  4  | from typing import Optional
> 4  5  |
> --------------------------------------------------------------------------------
> 12 13 | def multiple_1(arg1: Optional, arg2: Optional = None): ...
> 13 14 |
> 14 15 |
17,20c22,25
<    15 |+def multiple_2(arg1: Optional, arg2: Optional = None, arg3: int | None = None): ...
< 16 16 |
< 17 17 |
< 18 18 | def return_type(arg: Optional = None) -> Optional: ...
---
>    16 |+def multiple_2(arg1: Optional, arg2: Optional = None, arg3: int | None = None): ...
> 16 17 |
> 17 18 |
> 18 19 | def return_type(arg: Optional = None) -> Optional: ...
```

</details>

## Future work

This PR does not touch UP006, UP007, or UP045, which are currently
coupled to FA100. If this new approach turns out well, we may eventually
want to deprecate FA100 and add a `__future__` import in those rules'
fixes too.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-07-16 08:50:52 -04:00
Dylan 00e7d1ffd6
[`pycodestyle`] Handle brace escapes for t-strings in logical lines (#19358)
Tracks both f and t-strings in the logical line rules for `pycodestyle`.

Progress towards #15506
2025-07-15 14:48:48 +00:00
Dylan 92a302e291
[`pylint`] Extend invalid string character rules to include t-strings (#19355)
Handle t-strings in PLE2510-15

Progress towards #15506
2025-07-15 07:59:51 -05:00
Dylan 82391b5675
[`flake8-use-pathlib`] Skip single dots for `invalid-pathlib-with-suffix` (`PTH210`) on versions >= 3.14 (#19331)
Skips [invalid-pathlib-with-suffix
(PTH210)](https://docs.astral.sh/ruff/rules/invalid-pathlib-with-suffix/#invalid-pathlib-with-suffix-pth210)
for `.with_suffix(".")` on Python versions 3.14 and greater, as per [the
docs](https://docs.python.org/3.14/library/pathlib.html#pathlib.PurePath.with_suffix).

Progress towards #15506
2025-07-15 07:05:00 -05:00
Dylan 464144f1c6
[`ruff`] Allow `strict` kwarg when checking for `starmap-zip` (`RUF058`) in Python 3.14+ (#19333)
In Python 3.14 the keyword-argument `strict` was [added to
`map`](https://docs.python.org/3.14/library/functions.html#map). This PR
adds support for this when replacing a starmap-zip call with map in
[starmap-zip
(RUF058)](https://docs.astral.sh/ruff/rules/starmap-zip/#starmap-zip-ruf058).

Progress towards #15506
2025-07-15 07:04:23 -05:00
GiGaGon 966fd6f57a
[`pydoclint`] Fix `SyntaxError` from fixes with line continuations (`D201`, `D202`) (#19246)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

This PR fixes #7172 by suppressing the fixes for
[docstring-missing-returns
(DOC201)](https://docs.astral.sh/ruff/rules/docstring-missing-returns/#docstring-missing-returns-doc201)
/ [docstring-extraneous-returns
(DOC202)](https://docs.astral.sh/ruff/rules/docstring-extraneous-returns/#docstring-extraneous-returns-doc202)
if there is a surrounding line continuation character `\` that would
make the fix cause a syntax error.

To do this, the lints are changed from `AlwaysFixableViolation` to
`Violation` with `FixAvailability::Sometimes`.

In the case of `DOC201`, the fix is not given if the non-break line ends
in a line continuation character `\`. Note that lines are iterated in
reverse from the docstring to the function definition.

In the case of `DOC202`, the fix is not given if the docstring ends with
a line continuation character `\`.

## Test Plan

<!-- How was it tested? -->

Added a test case.
2025-07-14 13:31:36 -04:00
Dylan 2a2cc37158
Add t-string fixtures for rules that do not need to be modified (#19146)
I used a script to attempt to identify those rules with the following
property: changing f-strings to t-strings in the corresponding fixture
altered the number of lint errors emitted. In other words, those rules
for which f-strings and t-strings are not treated the same in the
current implementation.

This PR documents the subset of such rules where this is fine and no
changes need to be made to the implementation of the rule. Mostly these
are the rules where it is relevant that an f-string evaluates to type
`str` at runtime whereas t-strings do not.

In theory many of these fixtures are not super necessary - it's unlikely
t-strings would be used for most of these. However, the internal
handling of t-strings is tightly coupled with that of f-strings, and may
become even more so as we implement the upcoming changes due to
https://github.com/python/cpython/pull/135996 . So I'd like to keep
these around as regression tests.

Note: The `flake8-bandit` fixtures were already added during the
original t-string implementation.

| Rule(s) | Reason |
| --- | --- |
| [`unused-method-argument`
(`ARG002`)](https://docs.astral.sh/ruff/rules/unused-method-argument/#unused-method-argument-arg002)
| f-strings exempted for msg in `NotImplementedError` not relevant for
t-strings |
| [`logging-f-string`
(`G004`)](https://docs.astral.sh/ruff/rules/logging-f-string/#logging-f-string-g004)
| t-strings cannot be used here |
| [`f-string-in-get-text-func-call`
(`INT001`)](https://docs.astral.sh/ruff/rules/f-string-in-get-text-func-call/#f-string-in-get-text-func-call-int001)
| rule justified by eager evaluation of interpolations |
| [`flake8-bandit`](https://docs.astral.sh/ruff/rules/#flake8-bandit-s)|
rules justified by eager evaluation of interpolations |
| [`single-string-slots`
(`PLC0205`)](https://docs.astral.sh/ruff/rules/single-string-slots/#single-string-slots-plc0205)
| t-strings cannot be slots in general |
| [`unnecessary-encode-utf8`
(`UP012`)](https://docs.astral.sh/ruff/rules/unnecessary-encode-utf8/#unnecessary-encode-utf8-up012)
| cannot encode t-strings |
| [`no-self-use`
(`PLR6301`)](https://docs.astral.sh/ruff/rules/no-self-use/#no-self-use-plr6301)
| f-strings exempted for msg in NotImplementedError not relevant for
t-strings |
| [`pytest-raises-too-broad`
(`PT011`)](https://docs.astral.sh/ruff/rules/pytest-raises-too-broad/) /
[`pytest-fail-without-message`
(`PT016`)](https://docs.astral.sh/ruff/rules/pytest-fail-without-message/#pytest-fail-without-message-pt016)
/ [`pytest-warns-too-broad`
(`PT030`)](https://docs.astral.sh/ruff/rules/pytest-warns-too-broad/#pytest-warns-too-broad-pt030)
| t-strings cannot be empty or used as messages |
| [`assert-on-string-literal`
(`PLW0129`)](https://docs.astral.sh/ruff/rules/assert-on-string-literal/#assert-on-string-literal-plw0129)
| t-strings are not strings and cannot be empty |
| [`native-literals`
(`UP018`)](https://docs.astral.sh/ruff/rules/native-literals/#native-literals-up018)
| t-strings are not native literals |
2025-07-14 09:46:31 -05:00
w0nder1ng 26f736bc46
[`pep8_naming`] Avoid false positives on standard library functions with uppercase names (`N802`) (#18907)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-07-14 08:26:57 +00:00
Dan Parizher ee88abf77c
[`flake8_django`] Fix DJ008 false positive for abstract models with type-annotated `abstract` field (#19221)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-07-11 16:50:59 +00:00
Dan Parizher 110765154f
[`flake8-bugbear`] Fix B017 false negatives for keyword exception arguments (#19217)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-07-11 16:43:09 +00:00
Dan Parizher 30ee44770d
Fix I002 import insertion after docstring with multiple string statements (#19222) 2025-07-11 18:35:41 +02:00
Victor Hugo Gomes f2ae12bab3
[`flake8-return`] Fix false-positive for variables used inside nested functions in `RET504` (#18433)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
This PR is the same as #17656.

I accidentally deleted the branch of that PR, so I'm creating a new one.

Fixes #14052

## Test Plan

Add regression tests
<!-- How was it tested? -->
2025-07-10 16:10:22 -04:00
frank 83b5bbf004
Treat form feed as valid whitespace before a line continuation (#19220)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-07-10 14:09:34 +00:00
Dan Parizher 221edcba5c
[`pyupgrade`] Keyword arguments in `super` should suppress the `UP008` fix (#19131)
## Summary

Fixes #19096
2025-07-09 15:13:22 -04:00
Dan Parizher 5eb5ec987d
[`flake8-bugbear`] Support non-context-manager calls in `B017` (#19063)
## Summary

Fixes #19050

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-07-08 15:04:55 -04:00
chiri e23780c2e1
[`flake8-use-pathlib`] Add autofixes for `PTH203`, `PTH204`, `PTH205` (#18922)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
Part of #2331 |
[#18763](https://github.com/astral-sh/ruff/pull/18763#issuecomment-2988340436)
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
update snapshots
<!-- How was it tested? -->
2025-07-07 16:56:21 -04:00
GiGaGon 47f88b3008
[`flake8-type-checking`] Fix syntax error introduced by fix (`TC008`) (#19150)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

I noticed this while working on #18972. If the string targeted by
[quoted-type-alias
(TC008)](https://docs.astral.sh/ruff/rules/quoted-type-alias/#quoted-type-alias-tc008)
is a multiline string, the fix would introduce a syntax error. This PR
fixes that by adding parenthesis around the resulting replacement if the
string contained any newline characters (`\n`, `\r`) if it doesn't
already have parenthesis outside `("""...""")` or inside `"""(...)"""`
the annotation.

Failing examples:
https://play.ruff.rs/8793eb95-860a-4bb3-9cbc-6a042fee2946
```
PS D:\rust_projects\ruff> Get-Content issue.py
```
```py
from typing import TypeAlias

OptInt: TypeAlias = """int
| None"""

type OptInt = """int
| None"""
```
```
PS D:\rust_projects\ruff> uvx ruff check issue.py --isolated --select TC008 --fix --diff --preview
```
```

error: Fix introduced a syntax error. Reverting all changes.

This indicates a bug in Ruff. If you could open an issue at:

    https://github.com/astral-sh/ruff/issues/new?title=%5BFix%20error%5D

...quoting the contents of `issue.py`, the rule codes TC008, along with the `pyproject.toml` settings and executed command, we'd be very appreciative!
```

This PR also makes the example error out-of-the-box for #18972

Old example: https://play.ruff.rs/f6cd5adb-7f9b-444d-bb3e-8c045241d93e
```py
OptInt: TypeAlias = "int | None"
```

New example: https://play.ruff.rs/906c1056-72c0-4777-b70b-2114eb9e6eaf
```py
from typing import TypeAlias

OptInt: TypeAlias = "int | None"
```

The import was also added to the "Use instead" section.

## Test Plan

<!-- How was it tested? -->

Added multiple test cases
2025-07-07 15:34:14 -05:00
GiGaGon a33cff2b12
Fix F701 to F707 errors in tests (#19125)
## Summary

Per @ntBre in https://github.com/astral-sh/ruff/pull/19111, it would be
a good idea to make the tests no longer have these syntax errors, so
this PR updates the tests and snapshots.

`B031` gave me a lot of trouble since the ending test of declaring a
function named `groupby` makes it so that inside other functions, it's
unclear which `groupby` is referred to since it depends on when the
function is called. To fix it I made each function have it's own `from
itertools import groupby` so there's no more ambiguity.
2025-07-04 13:43:18 -05:00
Brent Westbrook b00f68a23c
[`ruff`] Allow more `field` calls from `attrs` (`RUF009`) (#19021)
Summary
--

Closes #19014 by identifying more `field` functions from `attrs`. We
already detected these when imported from `attrs` but not the `attr`
module from the same package. These functions are identical to the
`attrs` versions:

```pycon
>>> import attrs, attr
>>> attrs.field is attr.field
True
>>> attrs.Factory is attr.Factory
True
>>>
```

Test Plan
--

Regression tests based on the issue
2025-07-03 10:29:55 -04:00
GiGaGon cc736c3a51
[`refurb`] Fix false positive on empty tuples (`FURB168`) (#19058)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

This PR fixes #19047 / the [isinstance-type-none
(FURB168)](https://docs.astral.sh/ruff/rules/isinstance-type-none/#isinstance-type-none-furb168)
tuple false positive by adding a check if the tuple is empty to the
code. I also noticed there was another false positive with the other
tuple check in the same function, so I fixed it the same way.
`Union[()]` is invalid at runtime with `TypeError: Cannot take a Union
of no types.`, but it is accepted by `basedpyright`
[playground](https://basedpyright.com/?pythonVersion=3.8&typeCheckingMode=all&code=GYJw9gtgBALgngBwJYDsDmUkQWEMoCqKSYKAsAFAgCmAbtQIYA2A%2BvAtQBREkoDanAJQBdQUA)
and is equivalent to `Never`, so I fixed it anyways. I'm getting on a
side tangent here, but it looks like MyPy doesn't accept it, and ty
[playground](https://play.ty.dev/c2c468b6-38e4-4dd9-a9fa-0276e843e395)
gives `@Todo`.

## Test Plan

<!-- How was it tested? -->

Added two test cases for the two false positives.
[playground](https://play.ruff.rs/a53afc21-9a1d-4b9b-9346-abfbeabeb449)
2025-07-01 10:26:41 -04:00
Илья Любавский 667dc62038
[`ruff`] Fix syntax error introduced for an empty string followed by a u-prefixed string (`UP025`) (#18899)
## Summary
/closes #18895
## Test Plan

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-07-01 09:34:08 -04:00
Robsdedude 28ab61d885
[`pyupgrade`] Avoid PEP-604 unions with `typing.NamedTuple` (`UP007`, `UP045`) (#18682)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
Make `UP045` ignore `Optional[NamedTuple]` as `NamedTuple` is a function
(not a proper type). Rewriting it to `NamedTuple | None` breaks at
runtime. While type checkers currently accept `NamedTuple` as a type,
they arguably shouldn't. Therefore, we outright ignore it and don't
touch or lint on it.

For a more detailed discussion, see the linked issue.

## Test Plan
Added examples to the existing tests.

## Related Issues
Fixes: https://github.com/astral-sh/ruff/issues/18619
2025-06-30 17:22:23 -04:00
Robsdedude 34052a1185
[`flake8-comprehensions`] Fix `C420` to prepend whitespace when needed (#18616)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
This PR fixes rule C420's fix. The fix replaces `{...}` with
`dict....(...)`. Therefore, if there is any identifier or such right
before the fix, the fix will fuse that previous token with `dict...`.

The example in the issue is
```python
0 or{x: None for x in "x"}
# gets "fixed" to
0 ordict.fromkeys(iterable)
```

## Related Issues

Fixes: https://github.com/astral-sh/ruff/issues/18599
2025-06-30 12:38:26 -04:00
Dan Parizher 9f0d3cca89
[`pydocstyle`] Fix D413 infinite loop for parenthesized docstring (#18930)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #18908
2025-06-30 10:49:13 -04:00
Robsdedude eb9d9c3646
[`perflint`] Fix `PERF403` panic on attribute or subscription loop variable (#19042)
## Summary

Fixes: https://github.com/astral-sh/ruff/issues/19005

## Test Plan

Reproducer from issue report plus some extra cases that would cause the
panic were added.
2025-06-30 10:47:49 -04:00
Robsdedude a3638b3adc
[`pyupgrade`] Mark `UP008` fix safe if no comments in range (#18683)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
Mark `UP008`'s fix safe if it won't delete comments.

## Relevant Issues
Fixes: https://github.com/astral-sh/ruff/issues/18533

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-06-30 09:42:05 -04:00
हिमांशु d78f18cda9
[`flake8-executable`] Allow `uvx` in shebang line (`EXE003`) (#18967)
## Summary
closes #18902 

## Test Plan
I have added a test case
2025-06-30 09:38:18 -04:00
Robsdedude 6802c4702f
[`flake8-pyi`] Expand `Optional[A]` to `A | None` (`PYI016`) (#18572)
## Summary
Under preview 🧪 I've expanded rule `PYI016` to also flag type
union duplicates containing `None` and `Optional`.

## Test Plan
Examples/tests have been added. I've made sure that the existing
examples did not change unless preview is enabled.

## Relevant Issues
* https://github.com/astral-sh/ruff/issues/18508 (discussing
introducing/extending a rule to flag `Optional[None]`)
* https://github.com/astral-sh/ruff/issues/18546 (where I discussed this
addition with @AlexWaygood)

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-06-27 15:43:11 +00:00
Victor Hugo Gomes a1579d82d0
[`pylint`] Fix `PLW0108` autofix introducing a syntax error when the lambda's body contains an assignment expression (#18678)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

This PR also supresses the fix if the assignment expression target
shadows one of the lambda's parameters.

Fixes #18675

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Add regression tests.
<!-- How was it tested? -->
2025-06-26 16:56:17 -04:00
Victor Hugo Gomes d00697621e
[`ruff`] Fix false positives and negatives in `RUF010` (#18690) 2025-06-26 17:53:52 +02:00
Victor Hugo Gomes 2362263d5e
[`pyflakes`] Mark `F504`/`F522`/`F523` autofix as unsafe if there's a call with side effect (#18839)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-26 08:48:29 +00:00
Nikolas Hearp 8b22992988
[`flake8-errmsg`] Extend `EM101` to support byte strings (#18867)
## Summary

Fixes #18765

## Test Plan

Added test
2025-06-25 10:53:56 -04:00
Brent Westbrook 5aab49880a
[`pylint`] Allow fix with comments and document performance implications (`PLW3301`) (#18936)
Summary
--

Closes #18849 by adding a `## Known issues` section describing the
potential performance issues when fixing nested iterables. I also
deleted the comment check since the fix is already unsafe and added a
note to the `## Fix safety` docs.

Test Plan
--

Existing tests, updated to allow a fix when comments are present since
the fix is already unsafe.
2025-06-25 09:29:23 -04:00
Victor Hugo Gomes 66dbea90f1
[`perflint`] Fix false negative in `PERF401` (#18866) 2025-06-25 10:44:32 +02:00
Robsdedude 2a0c5669f2
[`refurb`] Detect more exotic float literals in `FURB164` (#18925) 2025-06-25 09:08:25 +02:00
Robsdedude 919af9628d
[`pygrep_hooks`] Add `AsyncMock` methods to `invalid-mock-access` (`PGH005`) (#18547)
## Summary
This PR expands PGH005 to also check for AsyncMock methods in the same
vein. E.g., currently `assert mock.not_called` is linted. This PR adds
the corresponding async assertions `assert mock.not_awaited()`.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-06-24 17:27:21 -04:00
chiri 3220242dec
[`flake8-use-pathlib`] Add autofix for `PTH202` (#18763)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
/closes #2331
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
update snapshots
<!-- How was it tested? -->

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-06-24 17:58:31 +00:00
Victor Hugo Gomes ca7933804e
[`ruff`] Trigger `RUF037` for empty string and byte strings (#18862) 2025-06-24 08:26:28 +02:00
Igor Drokin da16e00751
[`pyupgrade`] Extend version detection to include `sys.version_info.major` (`UP036`) (#18633)
## Summary

Resolves #18165 

Added pattern `["sys", "version_info", "major"]` to the existing matches
for `sys.version_info` to ensure consistent handling of both the base
object and its major version attribute.

## Test Plan
`cargo nextest run` and `cargo insta test`

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-06-23 20:01:55 +00:00
Илья Любавский 885dc9091f
[`ruff`] Frozen Dataclass default should be valid (`RUF009`) (#18735)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
/closes #17424
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
2025-06-23 19:10:12 +00:00
Victor Hugo Gomes 9e9c4fe17b
[`flake8-simplify`] Fix `SIM911` autofix creating a syntax error (#18793)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
The fix would create a syntax error if there wasn't a space between the
`in` keyword and the following expression.
For example:
```python
for country, stars in(zip)(flag_stars.keys(), flag_stars.values()):...
```

I also noticed that the tests for `SIM911` were note being run, so I
fixed that.

Fixes #18776

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Add regression test
<!-- How was it tested? -->
2025-06-23 16:24:47 +02:00
Victor Hugo Gomes f4c6ff3f68
[`pylint`] Fix `PLC2801` autofix creating a syntax error (#18857)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
This PR fixes `PLC2801` autofix creating a syntax error due to lack of
padding if it is directly after a keyword.

Fixes https://github.com/astral-sh/ruff/issues/18813
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
Add regression test
<!-- How was it tested? -->
2025-06-23 10:15:53 -04:00
Vasco Schiavo ca8ed35275
[`flake8-simplify`] Preserve original behavior for `except ()` and bare `except` (`SIM105`) (#18213)
The PR addresses issue #18209.
2025-06-23 10:01:57 -04:00
Victor Hugo Gomes 291413b126
[`perflint`] Fix `PERF101` autofix creating a syntax error and mark autofix as unsafe if there are comments in the `list` call expr (#18803)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-23 11:51:46 +00:00
Dan Parizher 7ec7853cec
[`flake8-pytest-style`] PT001/PT023 fix makes syntax error on parenthesized decorator (#18782)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-23 13:46:15 +02:00
Victor Hugo Gomes 0ce022e64e
[`refurb`] Fix `FURB163` autofix creating a syntax error for `yield` expressions (#18756) 2025-06-23 10:13:03 +02:00
Victor Hugo Gomes 659ecba477
[`pylint`] Supress `PLE2510`/`2512`/`2513`/`2514`/`2515` autofix if the text contains an odd number of backslashes (#18856)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-23 10:11:51 +02:00
Victor Hugo Gomes 06a78d0bd0
[`pylint`] Fix `PLC1802` autofix creating a syntax error and mark autofix as unsafe if there's comments in the `len` call (#18836)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
I've also found another bug while fixing this, where the diagnostic
would not trigger if the `len` call argument variable was shadowed. This
fixed a few false negatives in the test cases.
Example:
```python
fruits = []
fruits = []
if len(fruits):  # comment
    ...
```

Fixes #18811
Fixes #18812
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
Add regression test
<!-- How was it tested? -->

---------

Co-authored-by: Charlie Marsh <crmarsh416@gmail.com>
2025-06-23 00:32:57 +00:00
Victor Hugo Gomes 9089493263
[`refurb`] Mark `FURB129` autofix as unsafe if there's comments in the `readlines` call (#18858) 2025-06-22 08:51:37 +01:00
Victor Hugo Gomes f32ae94bc3
[`pylint`] Mark `PLE0241` autofix as unsafe if there's comments in the base classes (#18832)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-21 17:20:39 +00:00
chiri da6cbeee60
[`flake8-raise`] Make fix unsafe if it deletes comments (`RSE102`) (#18788) 2025-06-21 19:09:40 +02:00
Victor Hugo Gomes d9266284df
Handle parenthesized arguments in `remove_argument` (#18805)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2025-06-20 21:24:41 +00:00
Robsdedude e36611c4d8
[`flake8_pyi`] Fix `PYI041`'s fix causing TypeError with `None | None | ...` (#18637)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
Fix `PYI041`'s fix turning `None | int | None | float` into `None | None
| float`, which raises a `TypeError` when executed.

The fix consists of making sure that the merged super-type is inserted
where the first type that is merged was before.

## Test Plan
Tests have been expanded with examples from the issue.

## Related Issue
Fixes https://github.com/astral-sh/ruff/issues/18298
2025-06-20 15:04:51 -04:00
Hmvp 49763a7f7c
[`flake8-logging`] Avoid false positive for `exc_info=True` outside `logger.exception` (`LOG014`) (#18737)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes https://github.com/astral-sh/ruff/issues/18726 by also checking if
its a literal and not only that it is truthy. See also the first comment
in the issue.

It would have been nice to check for inheritance of BaseException but I
figured that is not possible yet...

## Test Plan

I added a few tests for valid input to exc_info
2025-06-20 14:43:08 -04:00
Gideon 2910988b06
[`pylint`] Ignore __init__.py files in (PLC0414) (#18400)
## Summary

Ignore `__init__.py` files in `useless-import-alias` (PLC0414).
See discussion in #18365 and #6294: we want to allow redundant aliases
in `__init__.py` files, as they're almost always intentional explicit
re-exports.
Closes #18365
 Closes #6294

---------

Co-authored-by: Dylan <dylwil3@gmail.com>
2025-06-20 18:20:27 +00:00
Victor Hugo Gomes ffb09c84f2
[`flake8-simplify`] Fix false negatives for shadowed bindings (`SIM910`, `SIM911`) (#18794)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
I also noticed that the tests for SIM911 were note being run, so I fixed
that.

Fixes #18777
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
Add regression test
<!-- How was it tested? -->
2025-06-20 13:25:36 -04:00
Gene Parmesan Thomas 5b3a501fae
[`pylint`] Avoid flattening nested `min`/`max` when outer call has single argument (`PLW3301`) (#16885)
## Summary

Fixes false positives (and incorrect autofixes) in `nested-min-max`
(`PLW3301`) when the outer `min`/`max` call only has a single argument.
Previously the rule would flatten:

```python
min(min([2, 3], [4, 1]))
```

into `min([2, 3], [4, 1])`, changing the semantics. The rule now skips
any nested call when the outer call has only one positional argument.
The pylint fixture and snapshot were updated accordingly.

## Test Plan

Ran Ruff against the updated `nested_min_max.py` fixture:

```shell
cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/pylint/nested_min_max.py --no-cache --select=PLW3301 --preview
```

to verify that `min(min([2, 3], [4, 1]))` and `max(max([2, 4], [3, 1]))`
are no longer flagged. Updated the fixture and snapshot; all other
existing warnings remain unchanged. The code compiles and the unit tests
pass.

---

This PR was generated by an AI system in collaboration with maintainers:
@carljm, @ntBre

Fixes #16163

---------

Signed-off-by: Gene Parmesan Thomas <201852096+gopoto@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-06-20 09:35:09 -04:00
Deric Crago e66f182045
[`ruff`] Added `cls.__dict__.get('__annotations__')` check (`RUF063`) (#18233)
Added `cls.__dict__.get('__annotations__')` check for Python 3.10+ and
Python < 3.10 with `typing-extensions` enabled.

Closes #17853 

<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Added `cls.__dict__.get('__annotations__')` check for Python 3.10+ and
Python < 3.10 with `typing-extensions` enabled.

## Test Plan

`cargo test`

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-20 09:32:40 -04:00
Victor Hugo Gomes 97819f8a37
[`flake8-pytest-style`] Mark autofix for `PT001` and `PT023` as unsafe if there's comments in the decorator (#18792) 2025-06-20 08:23:59 +02:00
Dylan ce0a32aadb
[`flake8-comprehensions`] Handle template strings for comprehension fixes (#18710)
Essentially this PR ensures that when we do fixes like this:

```diff
- t"{set(f(x) for x in foo)}"
+ t"{ {f(x) for x in foo} }"
```
we are correctly adding whitespace around the braces. 

This logic is already in place for f-strings and just needed to be
generalized to interpolated strings.
2025-06-19 16:23:46 -05:00
Robsdedude 4e83db4d40
[`pylint`] Detect more exotic NaN literals in `PLW0177` (#18630)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-19 11:05:06 +00:00
Victor Hugo Gomes 136443b71b
[`flake8-async`] Mark autofix for `ASYNC115` as unsafe if the call expression contains comments (#18753)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-19 13:01:33 +02:00
Victor Hugo Gomes f7a741a99e
[`flake8-bugbear`] Mark autofix for `B004` as unsafe if the `hasattr` call expr contains comments (#18755) 2025-06-19 10:46:53 +00:00
Charlie Marsh 4c8d612120
Enforce `pytest` import for decorators (#18779) 2025-06-19 09:49:34 +00:00
Victor Hugo Gomes 65b288b45b
[`flake8-comprehension`] Mark autofix for `C420` as unsafe if there's comments inside the dict comprehension (#18768) 2025-06-19 09:43:05 +00:00
chiri 06da2c808f
[flake8-async] fix detection for large integer sleep durations in `ASYNC116` rule (#18767)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-19 09:37:20 +00:00
Frazer McLean f67ff33177
Add lint rule for calling chmod with non-octal integers (#18541)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-06-19 11:30:29 +02:00
Nikolas Hearp dcf0a8d4d7
Mark `RET501` fix unsafe if comments are inside (#18780)
Co-authored-by: Charlie Marsh <crmarsh416@gmail.com>
2025-06-19 11:12:12 +02:00
Victor Hugo Gomes cff5adf324
[`pyupgrade`] Suppress `UP008` diagnostic if `super` symbol is not builtin (#18688)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #18684
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Add regression test
<!-- How was it tested? -->
2025-06-16 15:09:31 -04:00
Nikolas Hearp 7880a20794
[pylint] Fix `PLW0128` to check assignment targets in square brackets and after asterisks (#18665)
## Summary

This fixes PLW0128 to check for redeclared assignments in square
brackets and after asterisks.

Fixes #18660
2025-06-16 15:02:30 -04:00
chiri 83b0cde2fc
[`refurb`] Make the fix for `FURB163` unsafe for `log2`, `log10`, `*args`, and deleted comments (#18645)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
/closes #18639
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
update snapshots
<!-- How was it tested? -->

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-06-16 18:13:47 +00:00
Denys Kyslytsyn c3aa965546
[`ruff`] Check for non-context-manager use of `pytest.raises`, `pytest.warns`, and `pytest.deprecated_call` (`RUF061`) (#17368)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

This PR aims to close #16605.

## Summary

This PR introduces a new rule (`RUF061`) that detects non-contextmanager
usage of `pytest.raises`, `pytest.warns`, and `pytest.deprecated_call`.
This pattern is discouraged and [was proposed in
flake8-pytest-style](https://github.com/m-burst/flake8-pytest-style/pull/332),
but the corresponding PR has been open for over a month without
activity.

Additionally, this PR provides an unsafe fix for simple cases where the
non-contextmanager form can be transformed into the context manager
form. Examples of supported patterns are listed in `RUF061_raises.py`,
`RUF061_warns.py`, and `RUF061_deprecated_call.py` test files.

The more complex case from the original issue (involving two separate
statements):
```python
excinfo = pytest.raises(ValueError, int, "hello")
assert excinfo.match("^invalid literal")
```
is getting fixed like this:
```python
with pytest.raises(ValueError) as excinfo:
    int("hello")
assert excinfo.match("^invalid literal")
```
Putting match in the raises call requires multi-statement
transformation, which I am not sure how to implement.

## Test Plan

<!-- How was it tested? -->

New test files were added to cover various usages of the
non-contextmanager form of pytest.raises, warns, and deprecated_call.
2025-06-16 13:03:54 -04:00
Juriah a842899862
[`flake8-pyi`] Fix `custom-typevar-for-self` with string annotations (`PYI019`) (#18311)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary
<!-- What's the purpose of the change? What does it do, and why? -->

Solves #18257 

## Test Plan

<!-- How was it tested? -->
Snapshots updated with some cases (negative, positive, mixed
annotations).
2025-06-16 10:47:17 -04:00
Brent Westbrook e4423044f8
[`ruff`] Validate arguments before offering a fix (`RUF056`) (#18631)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/18628 by avoiding a fix
if there are "unknown" arguments, including any keyword arguments and
more than the expected 2 positional arguments.

I'm a bit on the fence here because it also seems reasonable to avoid a
diagnostic at all. Especially in the final test case I added (`not
my_dict.get(default=False)`), the hint suggesting to remove
`default=False` seems pretty misleading. At the same time, I guess the
diagnostic at least calls attention to the call site, which could help
to fix the missing argument bug too.

As I commented on the issue, I double-checked that keyword arguments are
invalid as far back as Python 3.8, even though the positional-only
marker was only added to the
[docs](https://docs.python.org/3.11/library/stdtypes.html#dict.get) in
3.12 (link is to 3.11, showing its absence).

## Test Plan

New tests derived from the bug report

## Stabilization

This was planned to be stabilized in 0.12, and the bug is less severe
than some others, but if there's nobody opposed, I will plan **not to
stabilize** this one for now.
2025-06-13 23:07:02 +00:00
𝕂 793ff9bdbc
Fix false positive in for mutations in return statements (B909) (#18408)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes false positive in B909 (`loop-iterator-mutation`) where mutations
inside return/break statements were incorrectly flagged as violations.
The fix adds tracking for when mutations occur within return/break
statements and excludes them from violation detection, as they don't
cause the iteration issues B909 is designed to prevent.



## Test Plan

- Added test cases covering the reported false positive scenarios to
`B909.py`
  - Verified existing B909 tests continue to pass (no regressions)
  - Ran `cargo test -p ruff_linter --lib flake8_bugbear` successfully

Fixes #18399
2025-06-13 10:39:55 -04:00
Victor Hugo Gomes 76d9009a6e
[`pycodestyle`] Fix `E731` autofix creating a syntax error for expressions spanned across multiple lines (#18479) 2025-06-13 08:44:15 +02:00
Brent Westbrook 96171f41c2
[`ruff`] Handle extra arguments to `deque` (`RUF037`) (#18614)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/18612 by:
- Bailing out without a fix in the case of `*args`, which I don't think
we can fix reliably
- Using an `Edit::deletion` from `remove_argument` instead of an
`Edit::range_replacement` in the presence of unrecognized keyword
arguments

I thought we could always switch to the `Edit::deletion` approach
initially, but it caused problems when `maxlen` was passed positionally,
which we didn't have any existing tests for.

The replacement fix can easily delete comments, so I also marked the fix
unsafe in these cases and updated the docs accordingly.

## Test Plan

New test cases derived from the issue.

## Stabilization

These are pretty significant changes, much like those to PYI059 in
https://github.com/astral-sh/ruff/pull/18611 (and based a bit on the
implementation there!), so I think it probably makes sense to
un-stabilize this for the 0.12 release, but I'm open to other thoughts
there.
2025-06-12 09:07:17 -04:00
chiri dbb0d60caa
[`pyupgrade`] Fix `super(__class__, self)` detection in UP008 (super-call-with-parameters) (#18478) 2025-06-12 08:52:45 +02:00
Victor Hugo Gomes a863000cbc
[`flake8-return`] Fix `RET504` autofix generating a syntax error (#18428) 2025-06-11 13:38:42 +00:00
Robsdedude 0724bee59c
[`pyupgrade`] Don't offer fix for `Optional[None]` in non-pep604-annotation-optional (`UP045)` or non-pep604-annotation-union (`UP007`) (#18545) 2025-06-11 08:19:00 +02:00
Victor Hugo Gomes 2213698a5d
[`pep8-naming`] Suppress fix for `N804` and `N805` if the recommend name is already used (#18472) 2025-06-11 07:58:55 +02:00
chiri dc322d23dd
[`ruff`] skip fix for `RUF059` if dummy name is already bound (unused-unpacked-variable) (#18509) 2025-06-11 07:58:05 +02:00
Brent Westbrook 6051a118d1
[`flake8-pyi`] Avoid syntax error in the case of starred and keyword arguments (`PYI059`) (#18611)
## Summary

Fixes https://github.com/astral-sh/ruff/issues/18602 by:
1. Avoiding a fix when `*args` are present
2. Inserting the `Generic` base class right before the first keyword
argument, if one is present

In an intermediate commit, I also had special handling to avoid a fix in
the `**kwargs` case, but this is treated (roughly) as a normal keyword,
and I believe handling it properly falls out of the other keyword fix.

I also updated the `add_argument` utility function to insert new
arguments right before the keyword argument list instead of at the very
end of the argument list. This changed a couple of snapshots unrelated
to `PYI059`, but there shouldn't be any functional changes to other
rules because all other calls to `add_argument` were adding a keyword
argument anyway.

## Test Plan

Existing PYI059 cases, plus new tests based on the issue

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-06-10 12:27:06 -04:00
Dylan caf885c20a
[`ruff`] Preserve parentheses around `deque` in fix for `unnecessary-empty-iterable-within-deque-call` (`RUF037`) (#18598)
Closes #18552
2025-06-09 15:38:39 -05:00
Brent Westbrook 79006dfb52
[`refurb`] Parenthesize lambda and ternary expressions in iter (`FURB122`, `FURB142`) (#18592)
Summary
--

Fixes #18590 by adding parentheses around lambdas and if expressions in
`for` loop iterators for FURB122 and FURB142. I also updated the docs on
the helper function to reflect the part actually being parenthesized and
the new checks.

The `lambda` case actually causes a `TypeError` at runtime, but I think
it's still worth handling to avoid causing a syntax error.

```pycon
>>> s = set()
... for x in (1,) if True else (2,):
...     s.add(-x)
... for x in lambda: 0:
...     s.discard(-x)
...
Traceback (most recent call last):
  File "<python-input-0>", line 4, in <module>
    for x in lambda: 0:
             ^^^^^^^^^
TypeError: 'function' object is not iterable
```

Test Plan
--

New test cases based on the bug report

---------

Co-authored-by: Dylan <dylwil3@gmail.com>
2025-06-09 16:07:34 -04:00
Charlie Marsh 331821244b
Refactor fix in `readlines-in-for` (#18573)
## Summary

Post-merge feedback from https://github.com/astral-sh/ruff/pull/18542.
2025-06-08 20:10:13 -04:00
Charlie Marsh 301b9f4135
Add trailing space around `readlines` (#18542)
Closes https://github.com/astral-sh/ruff/issues/17683.
2025-06-08 12:00:30 -04:00
Victorien 33468cc8cc
[`pyupgrade`] Apply `UP035` only on py313+ for `get_type_hints()` (#18476) 2025-06-05 17:16:29 +01:00
chiri c0bb83b882
[`perflint`] fix missing parentheses for lambda and ternary conditions (PERF401, PERF403) (#18412)
Closes #18405
2025-06-05 09:57:08 -05:00
Denys Kyslytsyn e677863787
[`fastapi`] Avoid false positive for class dependencies (`FAST003`) (#18271)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Closes #17226.

This PR updates the `FAST003` rule to correctly handle [FastAPI class
dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/classes-as-dependencies/).
Specifically, if a path parameter is declared in either:

- a `pydantic.BaseModel` used as a dependency, or  
- the `__init__` method of a class used as a dependency,  

then `FAST003` will no longer incorrectly report it as unused.

FastAPI allows a shortcut when using annotated class dependencies -
`Depends` can be called without arguments, e.g.:

```python
class MyParams(BaseModel):
    my_id: int

@router.get("/{my_id}")
def get_id(params: Annotated[MyParams, Depends()]): ...
```
This PR ensures that such usage is properly supported by the linter.

Note: Support for dataclasses is not included in this PR. Let me know if
you’d like it to be added.

## Test Plan

Added relevant test cases to the `FAST003.py` fixture.
2025-06-02 14:34:50 -04:00
Dylan 9bbf4987e8
Implement template strings (#17851)
This PR implements template strings (t-strings) in the parser and
formatter for Ruff.

Minimal changes necessary to compile were made in other parts of the code (e.g. ty, the linter, etc.). These will be covered properly in follow-up PRs.
2025-05-30 15:00:56 -05:00
Wei Lee d65bd69963
[`airflow`] Add unsafe fix for module moved cases (`AIR312`) (#18363)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Follow up on https://github.com/astral-sh/ruff/pull/18093 and apply it
to AIR312

## Test Plan

<!-- How was it tested? -->

The existing test fixtures have been updated
2025-05-30 09:36:20 -04:00
Wei Lee 0c29e258c6
[`airflow`] Add unsafe fix for module moved cases (`AIR311`) (#18366)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Follow up on https://github.com/astral-sh/ruff/pull/18093 and apply it
to AIR311

---

Rules fixed
* `airflow.models.datasets.expand_alias_to_datasets` →
`airflow.models.asset.expand_alias_to_assets`
* `airflow.models.baseoperatorlink.BaseOperatorLink` →
`airflow.sdk.BaseOperatorLink`


## Test Plan

<!-- How was it tested? -->
The existing test fixtures have been updated
2025-05-30 09:27:14 -04:00
Wei Lee b5b6b657cc
[`airflow`] Add unsafe fix for module moved cases (`AIR301`) (#18367)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Follow up on https://github.com/astral-sh/ruff/pull/18093 and apply it
to AIR301

## Test Plan

<!-- How was it tested? -->

The existing test fixtures have been updated
2025-05-30 08:46:39 -04:00
Wei Lee 3445d1322d
[`airflow`] Add unsafe fix module moved cases (`AIR302`) (#18093)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Add utility functions `generate_import_edit` and
`generate_remove_and_runtime_import_edit` to generate the fix needed for
the airflow rules.

1. `generate_import_edit` is for the cases where the member name has
changed. (e.g., `airflow.datasts.Dataset` to `airflow.sdk.Asset`) It's
just extracted from the original logic
2. `generate_remove_and_runtime_import_edit` is for cases where the
member name has not changed. (e.g.,
`airflow.operators.pig_operator.PigOperator` to
`airflow.providers.apache.pig.hooks.pig.PigCliHook`) This is newly
introduced. As it introduced runtime import, I mark it as an unsafe fix.
Under the hook, it tried to find the original import statement, remove
it, and add a new import fix

---

* rules fix
* `airflow.sensors.external_task_sensor.ExternalTaskSensorLink` →
`airflow.providers.standard.sensors.external_task.ExternalDagLink`

## Test Plan

<!-- How was it tested? -->
The existing test fixtures have been updated
2025-05-29 16:30:40 -04:00
Marcus Näslund 9d3cad95bc
[`refurb`] Add coverage of `set` and `frozenset` calls (`FURB171`) (#18035)
## Summary

Adds coverage of using set(...) in addition to `{...} in
SingleItemMembershipTest.

Fixes #15792
(and replaces the old PR #15793)

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Updated unit test and snapshot.

Steps to reproduce are in the issue linked above.

<!-- How was it tested? -->
2025-05-29 14:59:49 -04:00
Victor Hugo Gomes 04dc48e17c
[`refurb`] Fix `FURB129` autofix generating invalid syntax (#18235)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #18231

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Snapshot tests
<!-- How was it tested? -->
2025-05-28 17:01:03 -04:00
vjurczenia 27743efa1b
[`pylint`] Implement `missing-maxsplit-arg` (`PLC0207`) (#17454)
## Summary

Implements  `use-maxsplit-arg` (`PLC0207`)

https://pylint.readthedocs.io/en/latest/user_guide/messages/convention/use-maxsplit-arg.html
> Emitted when accessing only the first or last element of str.split().
The first and last element can be accessed by using str.split(sep,
maxsplit=1)[0] or str.rsplit(sep, maxsplit=1)[-1] instead.

This is part of https://github.com/astral-sh/ruff/issues/970

## Test Plan

`cargo test`

Additionally compared Ruff output to Pylint:
```
pylint --disable=all --enable=use-maxsplit-arg crates/ruff_linter/resources/test/fixtures/pylint/missing_maxsplit_arg.py

cargo run -p ruff -- check crates/ruff_linter/resources/test/fixtures/pylint/missing_maxsplit_arg.py --no-cache --select PLC0207
```

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-05-28 20:46:30 +00:00
Victor Hugo Gomes e23d4ea027
[`flake8-bugbear`] Ignore `__debug__` attribute in `B010` (#18357)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #18353
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
Snapshot tests
2025-05-28 16:24:52 -04:00
Viktor Merkurev b60ba75d09
[flake8_use_pathlib]: Replace os.symlink with Path.symlink_to (PTH211) (#18337)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-28 12:39:05 +02:00
Max Mynter 6d210dd0c7
Add Autofix for ISC003 (#18256)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-28 09:30:51 +02:00
chiri 9ce83c215d
[`pyupgrade`]: new rule UP050 (`useless-class-metaclass-type`) (#18334)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-28 09:22:44 +02:00
Vasanth d078ecff37
[flake8_async] Refactor argument name resolution for async sleep func… (#18262)
Co-authored-by: Vasanth-96 <ramavath.naik@itilite.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-26 09:53:03 +00:00
Wei Lee d8a5b9de17
[`airflow`] Revise fix title `AIR3` (#18215) 2025-05-26 10:31:48 +01:00
chiri be76fadb05
[pyupgrade] make fix unsafe if it deletes comments (UP010, unnecessary-future-import) (#18291) 2025-05-25 12:44:21 +02:00
Max Mynter 02fd48132c
[ty] Don't warn `yield` not in function when `yield` is in function (#18008) 2025-05-21 18:16:25 +02:00
Wei Lee 236633cd42
[`airflow`] Update `AIR301` and `AIR311` with the latest Airflow implementations (#17985)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->



* Remove the following rules
    * name
* `airflow.auth.managers.base_auth_manager.is_authorized_dataset` →
`airflow.api_fastapi.auth.managers.base_auth_manager.is_authorized_asset`
*
`airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_dataset`
→
`airflow.providers.fab.auth_manager.fab_auth_manager.is_authorized_asset`
* Update the following rules
    * name
* `airflow.models.baseoperatorlink.BaseOperatorLink` →
`airflow.sdk.BaseOperatorLink`
* `airflow.api_connexion.security.requires_access` → "Use
`airflow.api_fastapi.core_api.security.requires_access_*` instead`"
* `airflow.api_connexion.security.requires_access_dataset`→
`airflow.api_fastapi.core_api.security.requires_access_asset`
* `airflow.notifications.basenotifier.BaseNotifier` →
`airflow.sdk.bases.notifier.BaseNotifier`
        * `airflow.www.auth.has_access`  → None
        * `airflow.www.auth.has_access_dataset` → None
        * `airflow.www.utils.get_sensitive_variables_fields`→ None
        * `airflow.www.utils.should_hide_value_for_key`→ None
    * class attribute
        * `airflow..sensors.weekday.DayOfWeekSensor`
            * `use_task_execution_day` removed
*
`airflow.providers.amazon.aws.auth_manager.aws_auth_manager.AwsAuthManager`
            * `is_authorized_dataset`
* Add the following rules
    * class attribute
* `airflow.auth.managers.base_auth_manager.BaseAuthManager` |
`airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager`
     * name
* `airflow.auth.managers.base_auth_manager.BaseAuthManager` →
`airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager` *
`is_authorized_dataset` → `is_authorized_asset`
* refactor
    * simplify unnecessary match with if else
    * rename Replacement::Name as Replacement::AttrName

## Test Plan

<!-- How was it tested? -->
The test fixtures have been revised and updated.
2025-05-19 13:28:04 -04:00
Wei Lee 99cb89f90f
[`airflow`] Move rules from `AIR312` to `AIR302` (#17940)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

In the later development of Airflow 3.0, backward compatibility was not
added for some cases. Thus, the following rules are moved back to AIR302

* airflow.hooks.subprocess.SubprocessResult →
airflow.providers.standard.hooks.subprocess.SubprocessResult
* airflow.hooks.subprocess.working_directory →
airflow.providers.standard.hooks.subprocess.working_directory
* airflow.operators.datetime.target_times_as_dates →
airflow.providers.standard.operators.datetime.target_times_as_dates
* airflow.operators.trigger_dagrun.TriggerDagRunLink →
airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunLink
* airflow.sensors.external_task.ExternalTaskSensorLink →
airflow.providers.standard.sensors.external_task.ExternalDagLink (**This
one contains a minor change**)
* airflow.sensors.time_delta.WaitSensor →
airflow.providers.standard.sensors.time_delta.WaitSensor

## Test Plan

<!-- How was it tested? -->
2025-05-19 13:20:21 -04:00
Victor Hugo Gomes f53c580c53
[`pylint`] Fix `PLW1514` not recognizing the `encoding` positional argument of `codecs.open` (#18109)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #18107
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Snapshot tests
<!-- How was it tested? -->
2025-05-15 16:17:07 -04:00
Dan Parizher 030a16cb5f
[`flake8-simplify`] Correct behavior for `str.split`/`rsplit` with `maxsplit=0` (`SIM905`) (#18075)
Fixes #18069

<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
  requests.)
- Does this pull request include references to any relevant issues?
-->

## Summary

This PR addresses a bug in the `flake8-simplify` rule `SIM905`
(split-static-string) where `str.split(maxsplit=0)` and
`str.rsplit(maxsplit=0)` produced incorrect results for empty strings or
strings starting/ending with whitespace. The fix ensures that the
linting rule's suggested replacements now align with Python's native
behavior for these specific `maxsplit=0` scenarios.

## Test Plan

1. Added new test cases to the existing
`crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM905.py`
fixture to cover the scenarios described in issue #18069.
2.  Ran `cargo test -p ruff_linter`.
3. Verified and accepted the updated snapshots for `SIM905.py` using
`cargo insta review`. The new snapshots confirm the corrected behavior
for `maxsplit=0`.
2025-05-14 14:20:18 -04:00
Wei Lee 2e94d37275
[`airflow`] Get rid of `Replacement::Name` and replace them with `Replacement::AutoImport` for enabling auto fixing (`AIR301`, `AIR311`) (#17941)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Similiar to https://github.com/astral-sh/ruff/pull/17941.

`Replacement::Name` was designed for linting only. Now, we also want to
fix the user code. It would be easier to replace it with a better
AutoImport struct whenever possible.

On the other hand, `AIR301` and `AIR311` contain attribute changes that
can still use a struct like `Replacement::Name`. To reduce the
confusion, I also updated it as `Replacement::AttrName`

Some of the original `Replacement::Name` has been replaced as
`Replacement::Message` as they're not directly mapping and the message
has now been moved to `help`


## Test Plan

<!-- How was it tested? -->

The test fixtures have been updated
2025-05-14 11:10:15 -04:00
Dimitri Papadopoulos Orfanos 1b4f7de840
[`pyupgrade`] Add `resource.error` as deprecated alias of `OSError` (`UP024`) (#17933)
## Summary

Partially addresses #17935.


[`resource.error`](https://docs.python.org/3/library/resource.html#resource.error)
is a deprecated alias of
[`OSError`](https://docs.python.org/3/library/exceptions.html#OSError).
> _Changed in version 3.3:_ Following [**PEP
3151**](https://peps.python.org/pep-3151/), this class was made an alias
of
[`OSError`](https://docs.python.org/3/library/exceptions.html#OSError).

Add it to the list of `OSError` aliases found by [os-error-alias
(UP024)](https://docs.astral.sh/ruff/rules/os-error-alias/#os-error-alias-up024).

## Test Plan

Sorry, I usually don't program in Rust. Could you at least point me to
the test I would need to modify?
2025-05-14 10:37:25 -04:00
Victor Hugo Gomes 9b52ae8991
[`flake8-pytest-style`] Don't recommend `usefixtures` for parametrize values in `PT019` (#17650)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary
Fixes #17599.

## Test Plan

Snapshot tests.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-05-14 10:31:42 -04:00
Victor Hugo Gomes 0d6fafd0f9
[`flake8-bugbear`] Ignore `B028` if `skip_file_prefixes` is present (#18047)
## Summary

Fixes #18011
2025-05-12 17:06:51 -05:00
Wei Lee 2eb2d5359b
[`airflow`] Apply try-catch guard to all AIR3 rules (`AIR3`) (#17887)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

If a try-catch block guards the names, we don't raise warnings. During
this change, I discovered that some of the replacement types were
missed. Thus, I extend the fix to types other than AutoImport as well

## Test Plan

<!-- How was it tested? -->

Test fixtures are added and updated.
2025-05-12 17:13:41 -04:00
Marcus Näslund b2d9f59937
[`ruff`] Implement a recursive check for `RUF060` (#17976)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
The existing implementation of RUF060 (InEmptyCollection) is not
recursive, meaning that although set([]) results in an empty collection,
the existing code fails it because set is taking an argument.

The updated implementation allows set and frozenset to take empty
collection as positional argument (which results in empty
set/frozenset).

## Test Plan

Added test cases for recursive cases + updated snapshot (see RUF060.py).

---------

Co-authored-by: Marcus Näslund <marcus.naslund@kognity.com>
2025-05-12 16:17:13 -04:00
Victor Hugo Gomes d7ef01401c
[`flake8-use-pathlib`] `PTH*` suppress diagnostic for all `os.*` functions that have the `dir_fd` parameter (#17968)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Fixes #17776.

This PR also handles all other `PTH*` rules that don't support file
descriptors.

## Test Plan

<!-- How was it tested? -->

Update existing tests.
2025-05-12 16:11:56 -04:00
Victor Hugo Gomes c9031ce59f
[`refurb`] Mark autofix as safe only for number literals in `FURB116` (#17692)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary
We can only guarantee the safety of the autofix for number literals, all
other cases may change the runtime behaviour of the program or introduce
a syntax error. For the cases reported in the issue that would result in
a syntax error, I disabled the autofix.

Follow-up of #17661. 

Fixes #16472.
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Snapshot tests.
<!-- How was it tested? -->
2025-05-12 16:08:12 -04:00
Victor Hugo Gomes 138ab91def
[`flake8-simplify`] Fix `SIM905` autofix for `rsplit` creating a reversed list literal (#18045)
## Summary

Fixes #18042
2025-05-12 14:53:08 -05:00
Victor Hugo Gomes 6b3ff6f5b8
[`flake8-pie`] Mark autofix for `PIE804` as unsafe if the dictionary contains comments (#18046)
## Summary

Fixes #18036
2025-05-12 10:16:59 -05:00
Max Mynter b765dc48e9
Skip S608 for expressionless f-strings (#17999) 2025-05-10 11:37:58 +01:00
omahs 882a1a702e
Fix typos (#17988)
Fix typos

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-05-09 14:57:14 -04:00
Wei Lee 25e13debc0
[`airflow`] extend `AIR311` rules (#17913)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

* `airflow.models.Connection` → `airflow.sdk.Connection`
* `airflow.models.Variable` → `airflow.sdk.Variable`

## Test Plan

<!-- How was it tested? -->

The test fixtures has been updated (see the first commit for easier
review)
2025-05-09 13:08:37 -04:00
Wei Lee aac862822f
[`airflow`] Fix `SQLTableCheckOperator` typo (`AIR302`) (#17946) 2025-05-08 14:34:55 +02:00
Victor Hugo Gomes c504001b32
[`pyupgrade`] Add spaces between tokens as necessary to avoid syntax errors in `UP018` autofix (#17648)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-05-07 09:34:08 +02:00
Marcus Näslund 76b6d53d8b
Add new rule InEmptyCollection (#16480)
## Summary

Introducing a new rule based on discussions in #15732 and #15729 that
checks for unnecessary in with empty collections.

I called it in_empty_collection and gave the rule number RUF060.

Rule is in preview group.
2025-05-06 13:52:07 +00:00
Max Mynter 178c882740
[semantic-syntax-tests] Add test fixtures for `AwaitOutsideAsyncFunction` (#17785)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->
Re: #17526 
## Summary
Add test fixtures for `AwaitOutsideAsync` and
`AsyncComprehensionOutsideAsyncFunction` errors.

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
This is a test. 

<!-- How was it tested? -->
2025-05-05 14:02:06 -04:00
Victor Hugo Gomes 5e2c818417
[`flake8-bandit`] Mark tuples of string literals as trusted input in `S603` (#17801)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Fixes #17798
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

Snapshot tests
<!-- How was it tested? -->
2025-05-05 10:50:44 -04:00
Wei Lee 6e9fb9af38
[`airflow`] Skip attribute check in try catch block (`AIR301`) (#17790)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Skip attribute check in try catch block (`AIR301`)

## Test Plan

<!-- How was it tested? -->
update
`crates/ruff_linter/resources/test/fixtures/airflow/AIR301_names_try.py`
2025-05-05 10:01:05 -04:00
Micha Reiser b51c4f82ea
Rename Red Knot (#17820) 2025-05-03 19:49:15 +02:00
Victor Hugo Gomes 097af060c9
[`refurb`] Fix false positive for float and complex numbers in `FURB116` (#17661) 2025-05-03 15:59:46 +02:00
Victor Hugo Gomes 3353d07938
[`flake8-use-pathlib`] Fix `PTH104`false positive when `rename` is passed a file descriptor (#17712)
## Summary
Contains the same changes to the semantic type inference as
https://github.com/astral-sh/ruff/pull/17705.

Fixes #17694
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
Snapshot tests.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-05-01 10:01:17 -04:00
Victor Hugo Gomes 67ef370733
[`flake8-use-pathlib`] Fix `PTH116` false positive when `stat` is passed a file descriptor (#17709)
Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-05-01 08:16:28 +02:00
Hans 0e85cbdd91
[`flake8-use-pathlib`] Avoid suggesting `Path.iterdir()` for `os.listdir` with file descriptor (`PTH208`) (#17715)
## Summary

Fixes: #17695

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-04-30 20:08:57 +05:30
Victor Hugo Gomes 8c68d30c3a
[`flake8-use-pathlib`] Fix `PTH123` false positive when `open` is passed a file descriptor from a function call (#17705)
## Summary
Includes minor changes to the semantic type inference to help detect the
return type of function call.

Fixes #17691

## Test Plan

Snapshot tests
2025-04-29 16:51:38 -04:00
Wei Lee 504fa20057
[`airflow`] Apply auto fixes to cases where the names have changed in Airflow 3 (`AIR302`) (#17553)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Apply auto fixes to cases where the names have changed in Airflow 3 in
AIR302 and split the huge test cases into different test cases based on
proivder

## Test Plan

<!-- How was it tested? -->

the test cases has been split into multiple for easier checking
2025-04-28 16:35:17 -04:00
Dylan 1e8881f9af
[`refurb`] Mark fix as safe for `readlines-in-for` (`FURB129`) (#17644)
This PR promotes the fix applicability of [readlines-in-for
(FURB129)](https://docs.astral.sh/ruff/rules/readlines-in-for/#readlines-in-for-furb129)
to always safe.

In the original PR (https://github.com/astral-sh/ruff/pull/9880), the
author marked the rule as unsafe because Ruff's type inference couldn't
quite guarantee that we had an `IOBase` object in hand. Some false
positives were recorded in the test fixture. However, before the PR was
merged, Charlie added the necessary type inference and the false
positives went away.

According to the [Python
documentation](https://docs.python.org/3/library/io.html#io.IOBase), I
believe this fix is safe for any proper implementation of `IOBase`:

>[IOBase](https://docs.python.org/3/library/io.html#io.IOBase) (and its
subclasses) supports the iterator protocol, meaning that an
[IOBase](https://docs.python.org/3/library/io.html#io.IOBase) object can
be iterated over yielding the lines in a stream. Lines are defined
slightly differently depending on whether the stream is a binary stream
(yielding bytes), or a text stream (yielding character strings). See
[readline()](https://docs.python.org/3/library/io.html#io.IOBase.readline)
below.

and then in the [documentation for
`readlines`](https://docs.python.org/3/library/io.html#io.IOBase.readlines):

>Read and return a list of lines from the stream. hint can be specified
to control the number of lines read: no more lines will be read if the
total size (in bytes/characters) of all lines so far exceeds hint. [...]
>Note that it’s already possible to iterate on file objects using for
line in file: ... without calling file.readlines().

I believe that a careful reading of our [versioning
policy](https://docs.astral.sh/ruff/versioning/#version-changes)
requires that this change be deferred to a minor release - but please
correct me if I'm wrong!
2025-04-28 09:39:55 -05:00
Victor Hugo Gomes ceb2bf1168
[`flake8-pyi`] Ensure `Literal[None,] | Literal[None,]` is not autofixed to `None | None` (`PYI061`) (#17659)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2025-04-28 12:23:29 +01:00
Victor Hugo Gomes 826b2c9ff3
[`pycodestyle`] Fix duplicated diagnostic in `E712` (#17651) 2025-04-28 08:31:16 +01:00
Wei Lee aba21a5d47
[`airflow`] Extend `AIR301` rule (#17598)
## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Add "airflow.operators.python.get_current_context" →
"airflow.sdk.get_current_context" rule

## Test Plan

<!-- How was it tested? -->

the test fixture has been updated accordingly
2025-04-25 12:49:32 -04:00
Wei Lee b6281a8805
[`airflow`] update existing `AIR302` rules with better suggestions (#17542)
## Summary

Even though the original suggestion works, they've been removed in later
version and is no longer the best practices.

e.g., many sql realted operators have been removed and are now suggested
to use SQLExecuteQueryOperator instead

## Test Plan

The existing test fixtures have been updated
2025-04-25 12:44:28 -04:00
Brent Westbrook 6d3b1d13d6
[`pylint`] Detect `global` declarations in module scope (`PLE0118`) (#17411)
Summary
--

While going through the syntax errors in [this comment], I was surprised
to see the error `name 'x' is assigned to before global declaration`,
which corresponds to [load-before-global-declaration (PLE0118)] and has
also been reimplemented as a syntax error (#17135). However, it looks
like neither of the implementations consider `global` declarations in
the top-level module scope, which is a syntax error in CPython:

```python
# try.py
x = None
global x
```

```shell
> python -m compileall -f try.py
Compiling 'try.py'...
***   File "try.py", line 2
    global x
    ^^^^^^^^
SyntaxError: name 'x' is assigned to before global declaration
```

I'm not sure this is the best or most elegant solution, but it was a
quick fix that passed all of our tests.

Test Plan
--

New PLE0118 test case.

[this comment]:
https://github.com/astral-sh/ruff/issues/7633#issuecomment-1740424031
[load-before-global-declaration (PLE0118)]:
https://docs.astral.sh/ruff/rules/load-before-global-declaration/#load-before-global-declaration-ple0118
2025-04-25 08:37:16 -04:00
Max Mynter 3f84e75e20
Add Semantic Error Test for LateFutureImport (#17612)
Adresses a question in #17526.

## Summary
Adds a syntax error test for `__future__` import not at top of file. 

## Question: 
Is this a redundant with
8d2c79276d/crates/ruff_linter/resources/test/fixtures/pyflakes/F404_0.py (L1-L8)
and
8d2c79276d/crates/ruff_linter/resources/test/fixtures/pyflakes/F404_1.py (L1-L5)

which test pyflake `F404`?
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan
This is a test
<!-- How was it tested? -->
2025-04-25 08:32:57 -04:00
Vasco Schiavo 4eecc40110
[`semantic-syntax-errors`] test for `LoadBeforeGlobalDeclaration` - ruff linter (#17592)
Hey @ntBre 

just one easy case to see if I understood the issue #17526 

Let me know if is this what you had in mind.
2025-04-24 16:14:33 -04:00
Max Mynter a01f25107a
[`pyupgrade`] Preserve parenthesis when fixing native literals containing newlines (`UP018`) (#17220) 2025-04-24 08:48:02 +02:00
Wei Lee b537552927
[`airflow`] Apply auto fixes to cases where the names have changed in Airflow 3 (`AIR301`) (#17355)
## Summary

Apply auto fixes to cases where the names have changed in Airflow 3

## Test Plan

Add `AIR301_names_fix.py` and `AIR301_provider_names_fix.py` test fixtures
2025-04-23 12:43:41 -04:00
David Salvisberg f36262d970
Fixes how the checker visits `typing.cast`/`typing.NewType` arguments (#17538) 2025-04-23 09:26:00 +02:00
w0nder1ng 9c0772d8f0
[`perflint`] Allow list function calls to be replaced with a comprehension (`PERF401`) (#17519)
This is an implementation of the discussion from #16719. 

This change will allow list function calls to be replaced with
comprehensions:

```python
result = list()
for i in range(3):
    result.append(i + 1)
# becomes
result = [i + 1 for i in range(3)]
```

I added a new test to `PERF401.py` to verify that this fix will now work
for `list()`.
2025-04-21 13:29:24 -04:00
Vasco Schiavo f8061e8b99
[`refurb`] Mark the `FURB161` fix unsafe except for integers and booleans (#17240)
The PR fixes #16457 .

Specifically, `FURB161` is marked safe, but the rule generates safe
fixes only in specific cases. Therefore, we attempt to mark the fix as
unsafe when we are not in one of these cases.

For instances, the fix is marked as aunsafe just in case of strings (as
pointed out in the issue). Let me know if I should change something.

---------

Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
2025-04-18 13:46:01 -04:00
w0nder1ng 08221454f6
[`perflint`] Implement fix for `manual-dict-comprehension` (`PERF403`) (#16719)
## Summary

This change adds an auto-fix for manual dict comprehensions. It also
copies many of the improvements from #13919 (and associated PRs fixing
issues with it), and moves some of the utility functions from
`manual_list_comprehension.rs` into a separate `helpers.rs` to be used
in both.

## Test Plan

I added a preview test case to showcase the new fix and added a test
case in `PERF403.py` to make sure lines with semicolons function. I
didn't yet make similar tests to the ones I added earlier to
`PERF401.py`, but the logic is the same, so it might be good to add
those to make sure they work.
2025-04-18 13:10:40 -04:00
Vasco Schiavo 5fec1039ed
[`pylint`] Make fix unsafe if it deletes comments (`PLR1730`) (#17459)
The PR addresses issue #17311

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-04-18 12:49:01 -04:00
Wei Lee 1a79722ee0
[`airflow`] Extend `AIR311` rules (#17422)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

* Extend the following AIR311 rules
* `airflow.io.path.ObjectStoragePath` → `airflow.sdk.ObjectStoragePath`
    * `airflow.io.storage.attach` → `airflow.sdk.io.attach`
    * `airflow.models.dag.DAG` → `airflow.sdk.DAG`
    * `airflow.models.DAG` → `airflow.sdk.DAG`
    * `airflow.decorators.dag` → `airflow.sdk.dag`
    * `airflow.decorators.task` → `airflow.sdk.task`
    * `airflow.decorators.task_group` → `airflow.sdk.task_group`
    * `airflow.decorators.setup` → `airflow.sdk.setup`
    * `airflow.decorators.teardown` → `airflow.sdk.teardown`

## Test Plan

<!-- How was it tested? -->

The test case has been added to the button of the existing test
fixtures, confirmed to be correct and later reorgnaized
2025-04-16 12:40:15 -04:00
Wei Lee e6a2de3ac6
[`airflow`] Extract `AIR311` from `AIR301` rules (`AIR301`, `AIR311`) (#17310)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

As discussed in
https://github.com/astral-sh/ruff/issues/14626#issuecomment-2766146129,
we're to separate suggested changes from required changes.

The following symbols have been moved to AIR311 from AIR301. They still
work in Airflow 3.0, but they're suggested to be changed as they're
expected to be removed in a future version.

* arguments
    * `airflow..DAG | dag`
        * `sla_miss_callback`
    * operators
        * `sla`
* name
* `airflow.Dataset] | [airflow.datasets.Dataset` → `airflow.sdk.Asset`
    * `airflow.datasets, rest @ ..`
        * `DatasetAlias` → `airflow.sdk.AssetAlias`
        * `DatasetAll` → `airflow.sdk.AssetAll`
        * `DatasetAny` → `airflow.sdk.AssetAny`
* `expand_alias_to_datasets` → `airflow.sdk.expand_alias_to_assets`
        * `metadata.Metadata` → `airflow.sdk.Metadata`
    <!--airflow.models.baseoperator-->
    * `airflow.models.baseoperator.chain` → `airflow.sdk.chain`
* `airflow.models.baseoperator.chain_linear` →
`airflow.sdk.chain_linear`
* `airflow.models.baseoperator.cross_downstream` →
`airflow.sdk.cross_downstream`
* `airflow.models.baseoperatorlink.BaseOperatorLink` →
`airflow.sdk.definitions.baseoperatorlink.BaseOperatorLink`
    * `airflow.timetables, rest @ ..`
* `datasets.DatasetOrTimeSchedule` → *
`airflow.timetables.assets.AssetOrTimeSchedule`
    * `airflow.utils, rest @ ..`
        <!--airflow.utils.dag_parsing_context-->
* `dag_parsing_context.get_parsing_context` →
`airflow.sdk.get_parsing_context`

## Test Plan

<!-- How was it tested? -->

The test fixture has been updated acccordingly
2025-04-16 11:06:57 -04:00
Brent Westbrook 014bb526f4
[syntax-errors] `await` outside async functions (#17363)
Summary
--

This PR implements detecting the use of `await` expressions outside of
async functions. This is a reimplementation of
[await-outside-async
(PLE1142)](https://docs.astral.sh/ruff/rules/await-outside-async/) as a
semantic syntax error.

Despite the rule name, PLE1142 also applies to `async for` and `async
with`, so these are covered here too.

Test Plan
--

Existing PLE1142 tests.

I also deleted more code from the `SemanticSyntaxCheckerVisitor` to
avoid changes in other parser tests.
2025-04-14 13:01:48 -04:00
Brent Westbrook da32a83c9f
[syntax-errors] `return` outside function (#17300)
Summary
--

This PR reimplements [return-outside-function
(F706)](https://docs.astral.sh/ruff/rules/return-outside-function/) as a
semantic syntax error.

These changes are very similar to those in
https://github.com/astral-sh/ruff/pull/17298.

Test Plan
--

New linter tests, plus existing F706 tests.
2025-04-11 17:05:54 +00:00
Brent Westbrook ffef71d106
[syntax-errors] `yield`, `yield from`, and `await` outside functions (#17298)
Summary
--

This PR reimplements [yield-outside-function
(F704)](https://docs.astral.sh/ruff/rules/yield-outside-function/) as a
semantic syntax error. Despite the name, this rule covers `yield from`
and `await` in addition to `yield`.

Test Plan
--

New linter tests, along with the existing F704 test.

---------

Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2025-04-11 10:16:23 -04:00
Wei Lee c87e3ccb2f
[`airflow`] Add missing `AIR302` attribute check (#17115)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

attribute check was missing in the previous implementation

e.g.

```python
from airflow.api.auth.backend import basic_auth

basic_auth.auth_current_user
```

This PR adds this kind of check.

## Test Plan

<!-- How was it tested? -->

The test case has been added to the button of the existing test
fixtures, confirmed to be correct and later reorgnaized
2025-04-09 13:58:41 -04:00
Wei Lee 7207c86971
[`airflow`] Extract `AIR312` from `AIR302` rules (`AIR302`, `AIR312`) (#17152)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

As discussed in
https://github.com/astral-sh/ruff/issues/14626#issuecomment-2766146129,
we're to separate suggested changes from required changes.

The following symbols has been moved to AIR312 from AIR302. They still
work in Airflow 3.0, but they're suggested to be changed as they're
expected to be removed in future version

```python
from airflow.hooks.filesystem import FSHook
from airflow.hooks.package_index import PackageIndexHook
from airflow.hooks.subprocess import (SubprocessHook, SubprocessResult, working_directory)
from airflow.operators.bash import BashOperator
from airflow.operators.datetime import BranchDateTimeOperator, target_times_as_dates
from airflow.operators.trigger_dagrun import TriggerDagRunLink, TriggerDagRunOperator
from airflow.operators.empty import EmptyOperator
from airflow.operators.latest_only import LatestOnlyOperator
from airflow.operators.python import (BranchPythonOperator, PythonOperator, PythonVirtualenvOperator, ShortCircuitOperator)
from airflow.operators.weekday import BranchDayOfWeekOperator
from airflow.sensors.date_time import DateTimeSensor, DateTimeSensorAsync
from airflow.sensors.external_task import ExternalTaskMarker, ExternalTaskSensor, ExternalTaskSensorLink
from airflow.sensors.filesystem import FileSensor
from airflow.sensors.time_sensor import TimeSensor, TimeSensorAsync
from airflow.sensors.time_delta import TimeDeltaSensor, TimeDeltaSensorAsync, WaitSensor
from airflow.sensors.weekday import DayOfWeekSensor
from airflow.triggers.external_task import DagStateTrigger, WorkflowTrigger
from airflow.triggers.file import FileTrigger
from airflow.triggers.temporal import DateTimeTrigger, TimeDeltaTrigger
```

## Test Plan

<!-- How was it tested? -->

The test fixture has been updated acccordingly

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-04-09 10:43:07 -04:00
Denys Kyslytsyn ed14dbb1a2
[`flake8-pie`] Avoid false positive for multiple assignment with `auto()` (`PIE796`) (#17274)
This fix closes #16868 

I noticed the issue is assigned, but the assignee appears to be actively
working on another pull request. I hope that’s okay!

<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

As of Python 3.11.1, `enum.auto()` can be used in multiple assignments.
This pattern should not trigger non-unique-enums check.
Reference: [Python docs on
enum.auto()](https://docs.python.org/3/library/enum.html#enum.auto)

This fix updates the check logic to skip enum variant statements where
the right-hand side is a tuple containing a call to `enum.auto()`.

## Test Plan

<!-- How was it tested? -->

The added test case uses the example from the original issue. It
previously triggered a false positive, but now passes successfully.
2025-04-08 15:53:27 -04:00
Brent Westbrook 058439d5d3
[syntax-errors] Async comprehension in sync comprehension (#17177)
Summary
--

Detect async comprehensions nested in sync comprehensions in async
functions before Python 3.11, when this was [changed].

The actual logic of this rule is very straightforward, but properly
tracking the async scopes took a bit of work. An alternative to the
current approach is to offload the `in_async_context` check into the
`SemanticSyntaxContext` trait, but that actually required much more
extensive changes to the `TestContext` and also to ruff's semantic
model, as you can see in the changes up to
31554b473507034735bd410760fde6341d54a050. This version has the benefit
of mostly centralizing the state tracking in `SemanticSyntaxChecker`,
although there was some subtlety around deferred function body traversal
that made the changes to `Checker` more intrusive too (hence the new
linter test).

The `Checkpoint` struct/system is obviously overkill for now since it's
only tracking a single `bool`, but I thought it might be more useful
later.

[changed]: https://github.com/python/cpython/issues/77527

Test Plan
--

New inline tests and a new linter integration test.
2025-04-08 12:50:52 -04:00
Wei Lee dc02732d4d
[`airflow`] Expand module path check to individual symbols (`AIR302`) (#17278)
## Summary

### Improvement
Expand the following moved module into individual symbols.

* airflow.triggers.temporal
* airflow.triggers.file
* airflow.triggers.external_task
* airflow.hooks.subprocess
* airflow.hooks.package_index
* airflow.hooks.filesystem
* airflow.sensors.weekday
* airflow.sensors.time_delta
* airflow.sensors.time_sensor
* airflow.sensors.date_time
* airflow.operators.weekday
* airflow.operators.datetime
* airflow.operators.bash 

This removes `Replacement::ImportPathMoved`.

## Fix
During the expansion, the following paths were also fixed

* airflow.sensors.s3_key_sensor.S3KeySensor →
airflow.providers.amazon.aws.sensors.S3KeySensor
* airflow.operators.sql.SQLThresholdCheckOperator →
airflow.providers.common.sql.operators.sql.SQLThresholdCheckOperator
* airflow.hooks.druid_hook.DruidDbApiHook →
airflow.providers.apache.druid.hooks.druid.DruidDbApiHook
* airflow.hooks.druid_hook.DruidHook →
airflow.providers.apache.druid.hooks.druid.DruidHook
* airflow.kubernetes.pod_generator.extend_object_field →
airflow.providers.cncf.kubernetes.pod_generator.extend_object_field
* airflow.kubernetes.pod_launcher.PodLauncher →
airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodLauncher
* airflow.kubernetes.pod_launcher.PodStatus →
airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodStatus
* airflow.kubernetes.pod_generator.PodDefaults →
airflow.providers.cncf.kubernetes.pod_generator.PodDefaults
* airflow.kubernetes.pod_launcher_deprecated.PodDefaults →
airflow.providers.cncf.kubernetes.pod_launcher_deprecated.PodDefaults

### Refactor
As many symbols are moved into the same module,
`SourceModuleMovedToProvider` is introduced for grouping similar logic

## Test Plan
2025-04-08 09:03:27 -04:00
Denys Kyslytsyn 97dd6d120c
[`flake8-pytest-style`] Avoid false positive for legacy form of `pytest.raises` (`PT011`) (#17231)
This fix closes #17026 

## Summary

The check for the `PytestRaisesTooBroad` rule is now skipped if there is
a second positional argument present, which means `pytest.raises` is
used as a function.

## Test Plan

Tested on the example from the issue, which now passes the check.
```Python3
pytest.raises(Exception, func, *func_args, **func_kwargs).match("error message")
```

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-08 09:24:47 +02:00
Max Mynter f1d4ba32cc
Fix RUF100 to detect unused file-level noqa directives with specific codes (#17042) (#17061)
Closes #17042

## Summary
This PR fixes the issue outlined in #17042 where RUF100 (unused-noqa)
fails to detect unused file-level noqa directives (`# ruff: noqa` or `#
ruff: noqa: {code}`).

The issue stems from two underlying causes:

1. For blanket file-level directives (`# ruff: noqa`), there's a
circular dependency: the directive exempts all rules including RUF100
itself, which prevents checking for usage. This isn't changed by this
PR. I would argue it is intendend behavior - a blanket `# ruff: noqa`
directive should exempt all rules including RUF100 itself.

2. For code-specific file-level directives (e.g. `# ruff: noqa: F841`),
the handling was missing in the `check_noqa` function. This is added in
this PR.

## Notes
- For file-level directives, the `matches` array is pre-populated with
the specified codes during parsing, unlike line-level directives which
only populate their `matches` array when actually suppressing
diagnostics. This difference requires the somewhat clunky handling of
both cases. I would appreciate guidance on a cleaner design :)

- A more fundamental solution would be to change how file-level
directives initialize the `matches` array in
`FileNoqaDirectives::extract()`, but that requires more substantial
changes as it breaks existing functionality. I suspect discussions in
#16483 are relevant for this.

## Test Plan
- Local verification
- Added a test case and fixture
2025-04-07 09:21:52 -05:00
Wei Lee 1e9e423362
[`airflow`] Update oudated `AIR301`, `AIR302` rules (#17123)
## Summary

Some of the migration rules has been changed during Airflow 3
development. The following are new AIR302 rules. Corresponding AIR301
has also been removed.

* airflow.sensors.external_task_sensor.ExternalTaskMarker →
airflow.providers.standard.sensors.external_task.ExternalTaskMarker
* airflow.sensors.external_task_sensor.ExternalTaskSensor →
airflow.providers.standard.sensors.external_task.ExternalTaskSensor
* airflow.sensors.external_task_sensor.ExternalTaskSensorLink →
airflow.providers.standard.sensors.external_task.ExternalTaskSensorLink
* airflow.sensors.time_delta_sensor.TimeDeltaSensor →
airflow.providers.standard.sensors.time_delta.TimeDeltaSensor
* airflow.operators.dagrun_operator.TriggerDagRunLink →
airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunLink
* airflow.operators.dagrun_operator.TriggerDagRunOperator →
airflow.providers.standard.operators.trigger_dagrun.TriggerDagRunOperator
* airflow.operators.python_operator.BranchPythonOperator →
airflow.providers.standard.operators.python.BranchPythonOperator
* airflow.operators.python_operator.PythonOperator →
airflow.providers.standard.operators.python.PythonOperator
* airflow.operators.python_operator.PythonVirtualenvOperator →
airflow.providers.standard.operators.python.PythonVirtualenvOperator
* airflow.operators.python_operator.ShortCircuitOperator →
airflow.providers.standard.operators.python.ShortCircuitOperator
* airflow.operators.latest_only_operator.LatestOnlyOperator →
airflow.providers.standard.operators.latest_only.LatestOnlyOperator
* airflow.sensors.date_time_sensor.DateTimeSensor →
airflow.providers.standard.sensors.DateTimeSensor
* airflow.operators.email_operator.EmailOperator →
airflow.providers.smtp.operators.smtp.EmailOperator
* airflow.operators.email.EmailOperator →
airflow.providers.smtp.operators.smtp.EmailOperator
* airflow.operators.bash.BashOperator →
airflow.providers.standard.operators.bash.BashOperator
* airflow.operators.EmptyOperator →
airflow.providers.standard.operators.empty.EmptyOperator

closes: https://github.com/astral-sh/ruff/issues/17103

## Test Plan

The test fixture has been updated and checked after each change and
later reorganized in the latest commit
2025-04-07 09:45:56 -04:00
Max Mynter 98b95c9c38
Implement `Invalid rule provided` as rule RUF102 with `--fix` (#17138)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

Closes #17084

## Summary
This PR adds a new rule (RUF102) to detect and fix invalid rule codes in
`noqa` comments.
Invalid rule codes in `noqa` directives serve no purpose and may
indicate outdated code suppressions.

This extends the previous behaviour originating from
`crates/ruff_linter/src/noqa.rs` which would only emit a warnigs.
With this rule a `--fix` is available.

The rule:
1. Analyzes all `noqa` directives to identify invalid rule codes
2. Provides autofix functionality to:
   - Remove the entire comment if all codes are invalid
   - Remove only the invalid codes when mixed with valid codes
3. Preserves original comment formatting and whitespace where possible

Example cases:
- `# noqa: XYZ111` → Remove entire comment (keep empty line)
- `# noqa: XYZ222, XYZ333` → Remove entire comment (keep empty line)
-  `# noqa: F401, INVALID123` → Keep only valid codes (`# noqa: F401`)

## Test Plan
- Added tests in
`crates/ruff_linter/resources/test/fixtures/ruff/RUF102.py` covering
different example cases.

<!-- How was it tested? -->

## Notes 
- This does not handle cases where parsing fails. E.g. `# noqa:
NON_EXISTENT, ANOTHER_INVALID` causes a `LexicalError` and the
diagnostic is not propagated and we cannot handle the diagnostic. I am
also unsure what proper `fix` handling would be and making the user
aware we don't understand the codes is probably the best bet.
- The rule is added to the Preview rule group as it's a new addition

## Questions
- Should we remove the warnings, now that we have a rule?
- Is the current fix behavior appropriate for all cases, particularly
the handling of whitespace and line deletions?
- I'm new to the codebase; let me know if there are rule utilities which
could have used but didn't.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-04-04 08:05:59 +00:00
Dylan d401a5440e
Control flow: `return` and `raise` (#17121)
We add support for `return` and `raise` statements in the control flow
graph: we simply add an edge to the terminal block, push the statements
to the current block, and proceed.

This implementation will have to be modified somewhat once we add
support for `try` statements - then we will need to check whether to
_defer_ the jump. But for now this will do!

Also in this PR: We fix the `unreachable` diagnostic range so that it
lumps together consecutive unreachable blocks.
2025-04-03 08:30:29 -05:00
Wei Lee 8833484b10
[`airflow`] Move `AIR302` to `AIR301` and `AIR303` to `AIR302` (#17151)
## Summary

Following up the discussion in
https://github.com/astral-sh/ruff/issues/14626#issuecomment-2766548545,
we're to reorganize airflow rules. Before this discussion happens, we
combine required changes and suggested changes in to one single error
code.

This PR first rename the original error code to the new error code as we
discussed. We will gradually extract suggested changes out of AIR301 and
AIR302 to AIR311 and AIR312 in the following PRs

## Test Plan

Except for file, error code rename, the test case should work as it used
to be.
2025-04-02 23:01:31 +05:30
Wei Lee f989c2c3af
[`airflow`] Add autofix infrastructure to `AIR302` name checks (#16965)
## Summary

Add autofix infrastructure to `AIR302` name checks and use this logic to 
fix`"airflow", "api_connexion", "security", "requires_access_dataset"`, `"airflow", "Dataset"` and `"airflow",
"datasets", "Dataset"`

## Test Plan

The existing test fixture reflects the update
2025-04-02 15:27:51 +00:00
trag1c c2512b4c50
[`flake8-bandit`] Mark `str` and `list[str]` literals as trusted input (`S603`) (#17136)
## Summary

Closes #17112. Allows passing in string and list-of-strings literals
into `subprocess.run` (and related) calls without marking them as
untrusted input:
```py
import subprocess

subprocess.run("true")

# "instant" named expressions are also allowed
subprocess.run(c := "ls")
```

## Test Plan

Added test cases covering new behavior, passed with `cargo nextest run`.
2025-04-02 11:22:37 -04:00
Wei Lee fc2a0950eb
[`airflow`] Extend `AIR302` with additional symbols (#17085)
## Summary

* ``airflow.auth.managers.base_auth_manager.is_authorized_dataset`` has
been moved to
``airflow.api_fastapi.auth.managers.base_auth_manager.is_authorized_asset``
in Airflow 3.0
* ``airflow.auth.managers.models.resource_details.DatasetDetails`` has
been moved to
``airflow.api_fastapi.auth.managers.models.resource_details.AssetDetails``
in Airflow 3.0
* Dag arguments `default_view` and `orientation` has been removed in
Airflow 3.0
* `airflow.models.baseoperatorlink.BaseOperatorLink` has been moved to
`airflow.sdk.definitions.baseoperatorlink.BaseOperatorLink` in Airflow
3.0
* ``airflow.notifications.basenotifier.BaseNotifier`` has been moved to
``airflow.sdk.BaseNotifier`` in Airflow 3.0
* ``airflow.utils.log.secrets_masker`` has been moved to
``airflow.sdk.execution_time.secrets_masker`` in Airflow 3.0
* ``airflow...DAG.allow_future_exec_dates`` has been removed in Airflow
3.0
* `airflow.utils.db.create_session` has een removed in Airflow 3.0
* `airflow.sensors.base_sensor_operator.BaseSensorOperator` has been
moved to `airflow.sdk.bases.sensor.BaseSensorOperator` removed Airflow
3.0
* `airflow.utils.file.TemporaryDirectory` has been removed in Airflow
3.0 and can be replaced by `tempfile.TemporaryDirectory`
* `airflow.utils.file.mkdirs` has been removed in Airflow 3.0 and can be
replaced by `pathlib.Path({path}).mkdir`

## Test Plan

Test fixture has been added for these changes
2025-04-02 20:38:52 +05:30
Wei Lee 33bd08f49b
[`airflow`] Move `AIR301` to `AIR002` (#16978)
## Summary

Unlike other AIR3XX rules, this best practice can be applied to Airflow
1 and Airflow 2 as well. Thus, we think it might make sense for use to
move it to AIR002 so that the first number of the error align to Airflow
version as possible to reduce confusion

## Test Plan

the test fixture has been updated
2025-04-02 20:37:35 +05:30
Mohammad Amin Ghasemi e1b5b0de71
[flake8-import-conventions] Add import `numpy.typing as npt` to default `flake8-import-conventions.aliases` (#17133)
## Summary
Adds import `numpy.typing as npt` to `default in
flake8-import-conventions.aliases`
Resolves #17028

## Test Plan
Manually ran local ruff on the altered fixture and also ran `cargo test`
2025-04-02 09:25:46 +02:00
Dylan aa93005d8d
Control flow graph: setup (#17064)
This PR contains the scaffolding for a new control flow graph
implementation, along with its application to the `unreachable` rule. At
the moment, the implementation is a maximal over-approximation: no
control flow is modeled and all statements are counted as reachable.
With each additional statement type we support, this approximation will
improve.

So this PR just contains:
- A `ControlFlowGraph` struct and builder
- Support for printing the flow graph as a Mermaid graph
- Snapshot tests for the actual graphs
- (a very bad!) reimplementation of `unreachable` using the new structs
- Snapshot tests for `unreachable`

# Instructions for Viewing Mermaid snapshots
Unfortunately I don't know how to convince GitHub to render the Mermaid
graphs in the snapshots. However, you can view these locally in VSCode
if you install an extension that supports Mermaid graphs in Markdown,
and then add this to your `settings.json`:

```json
  "files.associations": {
"*.md.snap": "markdown",
  }
  ```
2025-04-01 05:53:42 -05:00
Aarni Koskela 491a51960e
[`ruff`] Support slices in `RUF005` (#17078)
## Summary

Teaches `RUF005` to also consider slices for concatenation. Other
indexing (`foo[0] + [7, 8, 9] + bar[1]`) is explicitly not considered.

```diff
 foo = [4, 5, 6]
-bar = [1, 2, 3] + foo
-slicing1 = foo[:1] + [7, 8, 9]
-slicing2 = [7, 8, 9] + bar[1:]
-slicing3 = foo[:1] + [7, 8, 9] + bar[1:]
+bar = [1, 2, 3, *foo]
+slicing1 = [*foo[:1], 7, 8, 9]
+slicing2 = [7, 8, 9, *bar[1:]]
+slicing3 = [*foo[:1], 7, 8, 9, *bar[1:]]
```

## Test Plan

Manually tested (diff above from `ruff check --diff`), snapshot updated.
2025-03-31 09:09:39 -04:00
Wei Lee fa80e10aac
[airflow] fix typos in AIR302 implementation and test cases (#17082)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

* The following paths are wrong 
* `airflow.providers.amazon.auth_manager.avp.entities` should be
`airflow.providers.amazon.aws.auth_manager.avp.entities`
* `["airflow", "datasets", "manager", "dataset_manager"]` should be
fixed as `airflow.assets.manager` but not
`airflow.assets.manager.asset_manager`
* `["airflow", "datasets.manager", "DatasetManager"]` should be `
["airflow", "datasets", "manager", "DatasetManager"]` instead

## Test Plan

<!-- How was it tested? -->

the test fixture is updated accordingly
2025-03-31 10:42:04 +02:00
Wei Lee 30a5f69913
[airflow] fix missing or wrong test cases (AIR302) (#16968)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->
Improve AIR302 test cases

## Test Plan

<!-- How was it tested? -->
test fixtures have been updated accordingly
2025-03-31 10:22:45 +02:00
John Stilley c35f2bfe32
Fixing various spelling errors (#16924)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

This is a cleanup PR. I am fixing various English language spelling
errors. This is mostly in docs and docstrings.

## Test Plan

The usual CI tests were run. I tried to build the docs (though I had
some troubles there). The testing needs here are, I trust, very low
impact. (Though I would happily test more.)
2025-03-23 08:08:40 +00:00
Vasco Schiavo 999fd4f885
[`refurb`] Fix starred expressions fix (`FURB161`) (#16550)
The PR partially solves issue #16457

Specifically, it solves the following problem:

```text
$ cat >furb161_1.py <<'# EOF'
print(bin(*[123]).count("1"))
# EOF

$ python furb161_1.py
6

$ ruff --isolated check --target-version py310 --preview --select FURB161 furb161_1.py --diff 2>&1 | grep error:
error: Fix introduced a syntax error. Reverting all changes.
```

Now starred expressions are corrected handled.
2025-03-19 17:43:58 -04:00
Dylan 98fdc0ebae
[`flake8-executables`] Allow `uv run` in shebang line for `shebang-missing-python` (`EXE003`) (#16849)
Skip the lint for [shebang-missing-python
(EXE003)](https://docs.astral.sh/ruff/rules/shebang-missing-python/#shebang-missing-python-exe003)
if we find `uv run` on the shebang line.

Closes #13021
2025-03-19 10:35:07 -05:00
Kaxil Naik b7d232cf89
[`airflow`] Add `chain`, `chain_linear` and `cross_downstream` for `AIR302` (#16647)
## Summary

Similar to https://github.com/astral-sh/ruff/pull/16014. PR on Airflow
side: https://github.com/apache/airflow/pull/47639

## Test Plan

A test fixture has been updated
2025-03-18 11:08:45 +05:30
Mauro Fontana 4da6936ec4
[`flake8-bandit`] Allow raw strings in `suspicious-mark-safe-usage` (`S308`) #16702 (#16770)
## Summary
Stop flagging each invocation of `django.utils.safestring.mark_safe`
(also available at, `django.utils.html.mark_safe`) as an error.

Instead, allow string literals as valid uses for `mark_safe`.

Also, update the documentation, pointing at
`django.utils.html.format_html` for dynamic content generation use
cases.

Closes #16702 

## Test Plan
I verified several possible uses, but string literals, are still
flagged.

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-17 11:29:07 +01:00
Dylan 238ec39c56
[`refurb`] Avoid panicking `unwrap` in `verbose-decimal-constructor` (`FURB157`) (#16777) 2025-03-17 05:09:07 -05:00
Micha Reiser 92193a3254 Consider all `TYPE_CHECKING` symbols for type-checking blocks (#16669)
## Summary

This PR stabilizes the preview behavior introduced in
https://github.com/astral-sh/ruff/pull/15719 to recognize all symbols
named `TYPE_CHECKING` as type-checking
checks in `if TYPE_CHECKING` conditions. This ensures compatibility with
mypy and pyright.

This PR also stabilizes the new behavior that removes `if 0:` and `if
False` to be no longer considered type checking blocks.
Since then, this syntax has been removed from the typing spec and was
only used for Python modules that don't have a `typing` module
([comment](https://github.com/astral-sh/ruff/pull/15719#issuecomment-2612787793)).

The preview behavior was first released with Ruff 0.9.5 (6th of
February), which was about a month ago. There are no open issues or PRs
for the changed behavior


## Test Plan

The snapshots for `SIM108` change because `SIM108` ignored type checking
blocks but it can no
simplify `if 0` or `if False` blocks again because they're no longer
considered type checking blocks.

The changes in the `TC005` snapshot or only due to that `if 0` and `if
False` are no longer recognized as type checking blocks

<!-- How was it tested? -->
2025-03-13 15:37:37 +01:00
Dylan 8bd140c99d Make noqa parsing consistent and more robust (#16483)
# Summary
The goal of this PR is to address various issues around parsing
suppression comments by

1. Unifying the logic used to parse in-line (`# noqa`) and file-level
(`# ruff: noqa`) noqa comments
2. Recovering from certain errors and surfacing warnings in these cases

Closes #15682 
Supersedes #12811 
Addresses
https://github.com/astral-sh/ruff/pull/14229#discussion_r1835481018
Related: #14229 , #12809
2025-03-13 15:37:37 +01:00
David Salvisberg c0b1413ecd [`flake8-bandit`] Move `unsafe-markup-use` from `RUF035` to `S704` (#15957)
## Summary

`RUF035` has been backported into bandit as `S704` in this
[PR](https://github.com/PyCQA/bandit/pull/1225)

This moves the rule and its corresponding setting to the `flake8-bandit`
category

## Test Plan

`cargo nextest run`

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-13 15:37:37 +01:00
Vasco Schiavo 6d6e524b90
[flake8-bandit] Fix mixed-case hash algorithm names (S324) (#16552)
The PR solves issue #16525
2025-03-07 15:21:07 +00:00
InSync a3ae76edc0
[`pyupgrade`] Do not offer fix when at least one target is `global`/`nonlocal` (`UP028`) (#16451)
## Summary

Resolves #16445.

`UP028` is now no longer always fixable: it will not offer a fix when at
least one `ExprName` target is bound to either a `global` or a
`nonlocal` declaration.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-03-04 11:28:01 +01:00
Vasco Schiavo 4d92e20e81
[`pylint`] Convert a `code` keyword argument to a positional argument (`PLR1722`) (#16424)
The PR addresses issue #16396 .

Specifically:

- If the exit statement contains a code keyword argument, it is
converted into a positional argument.
- If retrieving the code from the exit statement is not possible, a
violation is raised without suggesting a fix.

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-03-03 09:20:57 -05:00
Vasco Schiavo 5d56c2e877
[flake8-builtins] Ignore variables matching module attribute names (A001) (#16454)
This PR (partially) addresses issue #16373
2025-03-03 11:10:23 +01:00
Jelle Zijlstra c80678a1c0
Add new rule RUF059: Unused unpacked assignment (#16449)
Split from F841 following discussion in #8884.

Fixes #8884.

<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

Add a new rule for unused assignments in tuples. Remove similar behavior
from F841.

## Test Plan

Adapt F841 tests and move them over to the new rule.

<!-- How was it tested? -->

---------

Co-authored-by: Micha Reiser <micha@reiser.io>
2025-03-03 10:51:36 +01:00
Adam Johnson 5ca6cc2cc8
Exempt unittest context methods for SIM115 rule (#16439) 2025-02-28 16:29:50 +00:00
InSync 671494a620
[`pylint`] Also reports `case np.nan`/`case math.nan` (`PLW0177`) (#16378)
## Summary

Resolves #16374.

`PLW0177` now also reports the pattern of a case branch if it is an
attribute access whose qualified name is that of either `np.nan` or
`math.nan`.

As the rule is in preview, the changes are not preview-gated.

## Test Plan

`cargo nextest run` and `cargo insta test`.
2025-02-26 13:50:21 -05:00
Vasco Schiavo b89d61bd05
[FURB156] Do not consider docstring(s) (#16391)
Co-authored-by: Micha Reiser <micha@reiser.io>
2025-02-26 16:30:13 +00:00
Vasco Schiavo 42a5f5ef6a
[PLW1507] Mark fix unsafe (#16343) 2025-02-24 13:42:44 +01:00
Vasco Schiavo b312b53c2e
[`flake8-pyi`] Mark `PYI030` fix unsafe when comments are deleted (#16322) 2025-02-23 21:22:14 +00:00
InSync c814745643
[`flake8-self`] Ignore attribute accesses on instance-like variables (`SLF001`) (#16149) 2025-02-23 10:00:49 +00:00