Compare commits

...

78 Commits

Author SHA1 Message Date
Zanie
d65addffa0 Use https instead of ssh for schemastore authentication 2023-11-09 08:42:10 -06:00
Dhruv Manilawala
9d167a1f5c Slice source code instead of generating it for EM fixes (#7746)
## Summary

This PR fixes the bug where the generated fix for `EM*` rules would
replace a
triple-quoted (f-)string with a single-quoted (f-)string. This changes
the
semantic of the string in case it contains a single-quoted string
literal. This
is especially evident with f-strings where the expression could contain
another
string within it. For example,

```python
f"""normal {"another"} normal"""
```

## Test Plan

Add test case for triple-quoted string and update the snapshots.

fixes: #6988
fixes: #7736
2023-11-09 05:22:15 +00:00
Charlie Marsh
9e184a9067 Revert "Avoid inserting trailing commas within f-strings" (#8576)
Reverts astral-sh/ruff#8574. This caused a bunch of ecosystem changes --
needs more work.
2023-11-09 05:02:04 +00:00
Charlie Marsh
9d1027c239 Fix permalink to convention setting (#8575) 2023-11-09 04:50:32 +00:00
Charlie Marsh
f499f0ca60 Avoid inserting trailing commas within f-strings (#8574)
Closes https://github.com/astral-sh/ruff/issues/8556.
2023-11-08 23:25:23 -05:00
Charlie Marsh
722687ad72 Detect runtime-evaluated base classes defined in the current file (#8572)
Closes https://github.com/astral-sh/ruff/issues/8250.

Closes https://github.com/astral-sh/ruff/issues/5486.
2023-11-08 22:38:06 -05:00
Dhruv Manilawala
4760af3dcb Avoid FURB113 autofix if comments are present (#8494)
This PR avoids creating the fix for `FURB113` if there are comments in
between the `append` calls.

fixes: #8105
2023-11-09 03:10:11 +00:00
doolio
4fdf97a95c Apply consistent code block labels (#8563)
This ensures the python label is used for all python code blocks for
consistency.

## Test Plan

Visual inspection of all changes via git client ensuring no other
changes were made in error.
2023-11-09 01:49:24 +00:00
doolio
0ea1076f85 Add missing config tabs (#8558) 2023-11-09 01:49:06 +00:00
Zanie Blue
3956f38999 Prepare release 0.1.5 (#8570)
[Rendered
CHANGELOG](https://github.com/astral-sh/ruff/blob/release/015/CHANGELOG.md#015)
2023-11-08 16:00:57 -06:00
Zanie Blue
fe9727ac38 Add rooster release management configuration and instructions (#8567)
I'd rather not be the only one who can easily generate our changelog
entries so I invested some time to get Rooster a bit further along and
add instructions.
2023-11-08 13:08:19 -06:00
Dosenpfand
3ebaca5246 Doc: Fix link to isort known-first-party (#8562) 2023-11-08 11:12:11 -05:00
Felix Williams
7391f74cbc Add hidden --extension to override inference of source type from file extension (#8373)
## Summary

This PR addresses the incompatibility with `jupyterlab-lsp` +
`python-lsp-ruff` arising from the inference of source type from file
extension, raised in #6847.

In particular it follows the suggestion in
https://github.com/astral-sh/ruff/issues/6847#issuecomment-1765724679 to
specify a mapping from file extension to source type.

The source types are

- python
- pyi
- ipynb

Usage:

```sh
ruff check --no-cache --stdin-filename Untitled.ipynb --extension ipynb:python
```

Unlike the original suggestion, `:` instead of `=` is used to associate
file extensions to language since that is what is used with
`--per-file-ignores` which is an existing option that accepts a mapping.

## Test Plan

2 tests added to `integration_test.rs` to ensure the override works as
expected

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2023-11-08 08:02:40 +05:30
Charlie Marsh
71e93a9fa4 Only flag flake8-trio rule when trio is present (#8550)
## Summary

Hoping to avoid some false positives by narrowing the scope of
https://github.com/astral-sh/ruff/pull/8534.
2023-11-07 22:27:58 +00:00
Kar Petrosyan
e2c7b1ece6 [TRIO] Add TRIO109 rule (#8534)
## Summary

Adds TRIO109 from the [flake8-trio
plugin](https://github.com/Zac-HD/flake8-trio).
Relates to: https://github.com/astral-sh/ruff/issues/8451
2023-11-07 17:13:01 -05:00
Charlie Marsh
621e98f452 Improve detail link contrast in dark mode (#8548)
Use our light-mode styling for links in that context.

<img width="627" alt="Screen Shot 2023-11-07 at 4 34 48 PM"
src="https://github.com/astral-sh/ruff/assets/1309177/1e30c3ac-18e2-4663-876c-75c6f8b67d53">

Closes https://github.com/astral-sh/ruff/issues/8519.
2023-11-07 21:41:34 +00:00
Kar Petrosyan
0126f74c29 Add TRIO110 rule (#8537)
## Summary

Adds TRIO110 from the [flake8-trio
plugin](https://github.com/Zac-HD/flake8-trio).
Relates to: https://github.com/astral-sh/ruff/issues/8451
2023-11-07 21:27:19 +00:00
Chaojie
fce9f63418 [flake8-bandit] Implement mako-templates (S702) (#8533)
See: https://github.com/astral-sh/ruff/issues/1646.
2023-11-07 20:58:43 +00:00
Charlie Marsh
ce549e75bc Update pre-commit documentation (#8545)
I got some feedback on Mastodon that it wasn't clear how to use the
linter and formatter together in pre-commit (mostly in the pre-commit
repo's documentation, which is even less clear, but the two should be
consistent).
2023-11-07 18:40:13 +00:00
Lukasz Piatkowski
03303a9edd Account for selector specificity when merging extend_unsafe_fixes and override extend_safe_fixes (#8444)
## Summary

Prior to this change `extend_unsafe_fixes` took precedence over
`extend_safe_fixes` selectors, so any conflicts were resolved in favour
of `extend_unsafe_fixes`. Thanks to that ruff were conservatively
assuming that if configs conlict the fix corresponding to selected rule
will be treated as unsafe.

After this change we take into account Specificity of the selectors. For
conflicts between selectors of the same Specificity we will treat the
corresponding fixes as unsafe. But if the conflicting selectors are of
different specificity the more specific one will win.

## Test Plan

Tests were added for the `FixSafetyTable` struct. The
`check_extend_unsafe_fixes_conflict_with_extend_safe_fixes_by_specificity`
integration test was added to test conflicting rules of different
specificity.

Fixes #8404

---------

Co-authored-by: Zanie <contact@zanie.dev>
2023-11-07 10:33:40 -06:00
Zanie Blue
7873ca38e5 Update applicability messages for clarity in tests (#8541)
These names are only ever displayed internally right now and we could be
clearer in our test snapshots.

The diff is kind of scary because all of the tests fixtures are updated.
2023-11-07 16:11:43 +00:00
Aarni Koskela
7dabc4598b Allow RUFF_NO_CACHE environment variable (like RUFF_CACHE_DIR) (#8538)
## Summary

Being able to set `--no-cache` without touching the command line makes
comparing formatter speed with e.g. Hyperfine a lot easier; Black allows
one to set `BLACK_CACHE_DIR=/dev/null`, but setting
`RUFF_CACHE_DIR=/dev/null` has Ruff choke:

```
error: Failed to initialize cache at /dev/null: Not a directory (os error 20)
error: Failed to initialize cache at /dev/null: Not a directory (os error 20)
warning: Failed to open cache file '/dev/null/0.1.4/18160934645386409287': Not a directory (os error 20)
```

Alternately, we could make a `/dev/null` (or `nul` on Windows) cache
directory imply `--no-cache`?

## Test Plan

None yet.
2023-11-07 08:35:28 -06:00
Andrew Gallant
6a1fa4778f Reject more syntactically invalid Python programs (#8524)
## Summary

This commit adds some additional error checking to the parser such that
assignments that are invalid syntax are rejected. This covers the
obvious cases like `5 = 3` and some not so obvious cases like `x + y =
42`.

This does add an additional recursive call to the parser for the cases
handling assignments. I had initially been concerned about doing this,
but `set_context` is already doing recursion during assignments, so I
didn't feel as though this was changing any fundamental performance
characteristics of the parser. (Also, in practice, I would expect any
such recursion here to be quite shallow since the recursion is done on
the target of an assignment. Such things are rarely nested much in
practice.)

Fixes #6895

## Test Plan

I've added unit tests covering every case that is detected as invalid on
an `Expr`.
2023-11-07 07:16:06 -05:00
Charlie Marsh
c3d6d5d006 Add singleton escape hatch to B008 documentation (#8501)
## Summary:

Closes: https://github.com/astral-sh/ruff/issues/8378.
2023-11-07 04:53:45 +00:00
qdegraaf
9a8400a287 Avoid raising TRIO115 violations for trio.sleep(...) calls with non-number values (#8532)
## Summary

Fixes bug in `TRIO115` where it would not `return` for values that were
not a `NumberLiteral` so
```python
x = "bla"
trio.sleep(x)
```
would set off a false positive

## Test Plan

Added test case to fixture
2023-11-06 16:49:12 -06:00
Juan Orduz
d71c65d0c8 Add PyMC Marketing to Users (#8529)
Add [PyMC-Marketing](https://github.com/pymc-labs/pymc-marketing) to
users. See https://github.com/pymc-labs/pymc-marketing/pull/424
2023-11-06 16:21:49 -06:00
doolio
7f92bfbc4a docs: Add missing toml config tabs (#8512) 2023-11-06 21:12:38 +00:00
Charlie Marsh
37301375c8 Make SIM118 fix as safe when the expression is a known dictionary (#8525)
## Summary

Given `key in obj.keys()`, `obj` _could_ be a dictionary, or it could be
another type that defines
a `.keys()` method. In the latter case, removing the `.keys()` attribute
could lead to a runtime error.

Previously, we marked all `SIM118` fixes as unsafe for this reason;
however, in preview, we now mark them as safe if we can
infer that the expression is a dictionary.

## Test Plan

Added a preview fixture.
2023-11-06 21:06:33 +00:00
Aarni Koskela
c07947bfac Add Pillow to Ruff users (#8523)
## Summary

See https://github.com/python-pillow/Pillow/pull/6966 :)

## Test Plan

Looked at the Markdown preview!
2023-11-06 12:59:06 -06:00
T-256
72964529a5 Skip ecosystem check when no changes detected (#8520)
<!--
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

For example, https://github.com/astral-sh/ruff/pull/8512 doesn't need
ecosystem check
<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
2023-11-06 12:18:20 -06:00
Charlie Marsh
eab8ca4d7e Add dedicated method to find typed binding (#8517)
## Summary

We have this pattern in a bunch of places, where we find the _only_
binding to a name (and return `None`) if it's bound multiple times. This
PR DRYs it up into a method on `SemanticModel`.
2023-11-06 11:25:32 -05:00
Zanie Blue
5b3e922050 Upgrade pre-commit dependencies (#8518) 2023-11-06 10:08:22 -06:00
Zanie Blue
311a7751f9 Ensure ecosystem project errors are properly fenced (#8516)
Fixes bug where errors could be unfenced resulting in hidden remaining
content

e.g. https://github.com/astral-sh/ruff/pull/8508#issuecomment-1794960132
2023-11-06 09:35:07 -06:00
Charlie Marsh
5e2bb8ca07 Add a Fix constructor that takes Applicability as an argument (#8514)
## Summary

If you want to create an edit with dynamic applicability, you have to
branch and repeat the edit entirely between the two branches. If you
further need the edit itself to be dynamic (e.g., perhaps you have a
single edit in one case, vs. multiple in another), you suddenly have
four branches. This PR just adds an alternate constructor that takes
applicability as an argument, as an escape hatch.
2023-11-06 09:45:10 -05:00
konsti
3c8d9d45fb Recommend project.requires-python over target-version (#8513)
**Summary** Recommend the standardized, shared `project.requires-python`
over ruff's custom `target-version`. See
https://mastodon.social/deck/@davidism@mas.to/111347072204727710

**Test Plan** Docs only change
2023-11-06 14:35:32 +00:00
dependabot[bot]
82c3c513d2 Bump codspeed-criterion-compat from 2.3.0 to 2.3.1 (#8508) 2023-11-06 14:32:40 +00:00
dependabot[bot]
f2dc01e3aa Bump bitflags from 2.4.0 to 2.4.1 (#8511) 2023-11-06 09:20:39 -05:00
dependabot[bot]
5349143fca Bump serde_json from 1.0.107 to 1.0.108 (#8510) 2023-11-06 09:20:30 -05:00
dependabot[bot]
b6f23d57aa Bump syn from 2.0.38 to 2.0.39 (#8509) 2023-11-06 09:19:47 -05:00
dependabot[bot]
b7b6e0136e Bump serde-wasm-bindgen from 0.6.0 to 0.6.1 (#8507) 2023-11-06 09:19:30 -05:00
Ofek Lev
218f517487 Fix typo in example (#8506) 2023-11-06 12:52:14 +05:30
Dhruv Manilawala
75c669a007 Fix tab configuration docs (#8502)
Otherwise it doesn't render as expected.
2023-11-06 03:02:45 +00:00
Shantanu
2d5ce4532a Flag all comparisons against builtin types in E721 (#8491)
See #8483. Generalised fix on top of #8485

Based on the output of `print("\n".join(k for k, v in
builtins.__dict__.items() if isinstance(v, type)))`
2023-11-05 21:28:47 -05:00
qdegraaf
f3e2d12609 [TRIO] Add TRIO115: TrioZeroSleepCall (#8486)
## Summary

Adds `TRIO115` from the [flake8-trio
plugin](https://github.com/Zac-HD/flake8-trio).

## Test Plan

Added a new fixture, based on [the one from upstream
plugin](https://github.com/Zac-HD/flake8-trio/blob/main/tests/eval_files/trio115.py)

## Issue link

Relates to: https://github.com/astral-sh/ruff/issues/8451
2023-11-06 01:19:46 +00:00
Tom Kuson
de2d7e97b1 [refurb] Implement type-none-comparison (FURB169) (#8487)
## Summary

Implement
[`no-is-type-none`](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/no_is_type_none.py)
as `type-none-comparison` (`FURB169`).

Auto-fixes comparisons that use `type` to compare the type of an object
to `type(None)` to a `None` identity check. For example,

```python
type(foo) is type(None)
```

becomes

```python
foo is None
```

Related to #1348.

## Test Plan

`cargo test`
2023-11-06 00:56:20 +00:00
Charlie Marsh
bcb737dd80 Add notes on fix safety to a few rules (#8500) 2023-11-06 00:48:57 +00:00
Charlie Marsh
8c146bbf11 Allow collapsed-ellipsis bodies in other statements (#8499)
## Summary

Black and Ruff's preview styles now collapse statements like:

```python
from contextlib import nullcontext

ctx = nullcontext()
with ctx: ...
```

Historically, we made an exception here for classes
(https://github.com/astral-sh/ruff/pull/2837). This PR extends it to
other statement kinds for consistency with the formatter.

Closes https://github.com/astral-sh/ruff/issues/8496.
2023-11-05 19:42:34 -05:00
qdegraaf
4170ef0508 [TRIO] Add TRIO105: SyncTrioCall (#8490)
## Summary

Adds `TRIO105` from the [flake8-trio
plugin](https://github.com/Zac-HD/flake8-trio). The `MethodName` logic
mirrors that of `TRIO100` to stay consistent within the plugin.

It is at 95% parity with the exception of upstream also checking for a
slightly more complex scenario where a call to `start()` on a
`trio.Nursery` context should also be immediately awaited. Upstream
plugin appears to just check for anything named `nursery` judging from
[the relevant issue](https://github.com/Zac-HD/flake8-trio/issues/56).

Unsure if we want to do so something similar or, alternatively, if there
is some capability in ruff to check for calls made on this context some
other way

## Test Plan

Added a new fixture, based on [the one from upstream
plugin](https://github.com/Zac-HD/flake8-trio/blob/main/tests/eval_files/trio105.py)

## Issue link

Refers: https://github.com/astral-sh/ruff/issues/8451
2023-11-05 19:56:10 +00:00
Chris Rose
72ebde8d38 Add instructions for configuration of Emacs (#8488)
## Summary

Add editor integration docs for `ruff format` in Emacs by way of the
Apheleia formatter library

Depends on:  https://github.com/radian-software/apheleia/issues/233
2023-11-05 17:15:59 +00:00
trag1c
1672a3d3b7 Added tabs for configuration files in the documentation (#8480)
## Summary

Closes #8384.

## Test Plan

Checked whether it renders properly on the `mkdocs serve` preview.
2023-11-05 17:10:29 +00:00
Tom Kuson
8c0d65c98e Fix F841 false negative on assignment to multiple variables (#8489)
## Summary

Closes #8441 behind preview feature flag.

## Test Plan

`cargo test`
2023-11-05 12:01:10 -05:00
Dhruv Manilawala
b3c2935fa5 Avoid D301 autofix for u prefixed strings (#8495)
This PR avoids creating the fix for `D301` if the string is prefixed
with `u` i.e., it's a unicode string. The reason being that `u` and `r`
cannot be used together as it's a syntax error.

Refer:
https://github.com/astral-sh/ruff/issues/8402#issuecomment-1788783287
2023-11-05 09:45:49 -05:00
Micha Reiser
e57bccd500 Fix multiline lambda expression statement formating (#8466)
## Summary

This PR fixes a bug in our formatter where a multiline lambda expression
statement was formatted over multiple lines without adding parentheses.

The PR "fixes" the problem by not splitting the lambda parameters if it
is not parenthesized

## Test Plan

Added test
2023-11-05 09:35:23 -05:00
qdegraaf
75c9be099f [E721] Flag comparisons to memoryview (#8485)
## Summary

Adds `memoryview` to the list of typeclasses that `fn is_type()` uses
for type comparison checks so that it raises a violation if `is`, `is
not` or `isinstance()` are not used.

## Test Plan

Added examples to existing fixture

## Issue Link

Closes: https://github.com/astral-sh/ruff/issues/8483
2023-11-04 13:41:58 +00:00
Charlie Marsh
c4889196e7 Add missing pyupgrade entry to changelog (#8479)
This got merged after the changelog was generated, but is part of the
release.
2023-11-03 20:57:19 +00:00
Charlie Marsh
6e635e99f4 Add changelog for v0.1.4 (#8478) 2023-11-03 20:11:21 +00:00
Charlie Marsh
260ea41975 Bump version to v0.1.4 (#8477) 2023-11-03 14:52:56 -04:00
Hugo van Kemenade
65effc6666 Add pyupgrade UP041 to replace TimeoutError aliases (#8476)
## Summary

Add UP041 to replace `TimeoutError` aliases:

* Python 3.10+: `socket.timeout`
* Python 3.11+: `asyncio.TimeoutError`

Re:

* https://github.com/asottile/pyupgrade#timeouterror-aliases
*
https://docs.python.org/3/library/asyncio-exceptions.html#asyncio.TimeoutError
* https://docs.python.org/3/library/socket.html#socket.timeout

Based on `os_error_alias.rs`.

## Test Plan

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

By running:

```
cargo clippy --workspace --all-targets --all-features -- -D warnings  # Rust linting
RUFF_UPDATE_SCHEMA=1 cargo test  # Rust testing and updating ruff.schema.json
pre-commit run --all-files --show-diff-on-failure  # Rust and Python formatting, Markdown and Python linting, etc.
cargo insta review
```

And also running with different `--target-version` values:

```sh
cargo run -p ruff_cli -- check crates/ruff_linter/resources/test/fixtures/pyupgrade/UP041.py --no-cache --select UP041 --target-version py37 --diff
cargo run -p ruff_cli -- check crates/ruff_linter/resources/test/fixtures/pyupgrade/UP041.py --no-cache --select UP041 --target-version py310 --diff
cargo run -p ruff_cli -- check crates/ruff_linter/resources/test/fixtures/pyupgrade/UP041.py --no-cache --select UP041 --target-version py311 --diff
```
2023-11-03 17:24:47 +00:00
T-256
4982694b54 D300: prevent autofix when both triples are in body (#8462)
## Summary
Addresses
https://github.com/astral-sh/ruff/issues/8402#issuecomment-1788782750

## Test Plan

Added associated test
2023-11-03 12:49:50 -04:00
Charlie Marsh
536ac550ed Remove trailing periods from NumPy 2.0 code actions (#8475)
Very minor consistency thing with other rules. For code actions, we tend
to say `Replace with {X}` rathern than `Use {X} instead.`
2023-11-03 16:28:06 +00:00
Charlie Marsh
f2335fe692 Make Unicode-to-Unicode confusables a preview change (#8473) 2023-11-03 12:17:28 -04:00
Charlie Marsh
b0f9a14d9a Mark byte_bounds as a non-backwards-compatible NumPy 2.0 change (#8474)
This is the one refactor in the NumPy 2.0 upgrade rule that isn't
compatible with earlier versions of NumPy, so I'm marking it as unsafe
and adding a dedicated message.
2023-11-03 12:14:57 -04:00
Deepyaman Datta
f56bc1983b Place 'r' prefix before 'f' for raw format strings (#8464)
## Summary

Currently, `UP032` applied to raw strings results in format strings with
the prefix 'fr'. This gets changed to 'rf' by Ruff format (or Black). In
order to avoid that, this PR uses the prefix 'rf' to begin with.

## Test Plan

Updated the expectation on an existing test.
2023-11-03 10:56:21 -04:00
Charlie Marsh
7c12eaf322 Use characters instead of u32 in confusable map (#8463) 2023-11-03 09:57:47 -04:00
Dhruv Manilawala
41e538a748 Provide example for exclusive linting or formatting Notebooks (#8461)
Reference screenshot: https://github.com/astral-sh/ruff/assets/67177269/eef5ab79-77e9-4ced-be7b-a61b7bb20ecd
2023-11-03 16:56:20 +05:30
Micha Reiser
dd2d8cb579 Avoid parenthesizing unsplittable because of comments (#8431) 2023-11-03 05:12:59 +00:00
Dhruv Manilawala
a08c5b7fa7 Upgrade PyYAML to 6.0.1 to avoid build error (#8460)
Refer: https://github.com/yaml/pyyaml/pull/702
2023-11-03 10:41:30 +05:30
Christopher Covington
9f30ccc1f4 Autoformat confusable units (#4430)
I've seen errors crop up from using the different micro and mu
characters. Follow matching recommendations on which character to prefer
for micro, ohm, and angstrom. References:
* Section 22.2 Letterlike Symbols, subsection Unit Symbols, page 877 of
[The Unicode Standard, Version 15.0

](https://www.unicode.org/versions/Unicode15.0.0/UnicodeStandard-15.0.pdf)
* Section 2.5 Duplicated Characters of [Unicode Technical Report
25](https://www.unicode.org/reports/tr25/)
* [SI
brochure](https://www.bipm.org/documents/20126/41483022/SI-Brochure-9-EN.pdf)
*
https://github.com/unicode-org/icu/blob/main/icu4c/source/data/unidata/confusables.txt
2023-11-03 04:58:43 +00:00
Charlie Marsh
31286e1c95 Re-run scripts/update_ambiguous_characters.py (#8459)
These weren't formatted consistently, and when I re-ran, the formatting
changed a bit, so I'm editing the script to keep that file constant.
2023-11-03 04:50:10 +00:00
Charlie Marsh
b9994dc495 Use fixedOverflowWidgets for playground popover (#8458)
After some Googling...

<img width="656" alt="Screen Shot 2023-11-03 at 12 23 09 AM"
src="https://github.com/astral-sh/ruff/assets/1309177/be6aaa3d-0068-4bad-a27f-01785179567d">

Closes https://github.com/astral-sh/ruff/issues/8442.
2023-11-03 04:29:37 +00:00
Micha Reiser
f16505d885 Formatter: Remove unnecessary group (#8455) 2023-11-03 04:14:29 +00:00
Mateusz Sokół
d04d964ace Implement NumPy 2.0 migration rule (#7702)
## Summary

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

Hi! Currently NumPy Python API is undergoing a cleanup process that will
be delivered in NumPy 2.0 (release is planned for the end of the year).
Most changes are rather simple (renaming, removing or moving a member of
the main namespace to a new place), and they could be flagged/fixed by
an additional ruff rule for numpy (e.g. changing occurrences of
`np.float_` to `np.float64`).

Would you accept such rule?  

I named it `NPY201` in the existing group, so people will receive a
heads-up for changes arriving in 2.0 before actually migrating to it.

~~This is still a draft PR.~~ I'm not an expert in rust so if any part
of code can be done better please share!

NumPy 2.0 migration guide:
https://numpy.org/devdocs/numpy_2_0_migration_guide.html
NEP 52: https://numpy.org/neps/nep-0052-python-api-cleanup.html
NumPy cleanup tracking issue:
https://github.com/numpy/numpy/issues/23999


## Test Plan

A unit test is provided that checks all rule's fix cases.
2023-11-03 03:47:01 +00:00
Charlie Marsh
f64c389654 Detect and ignore Jupyter automagics (#8398)
## Summary

LangChain is attempting to use Ruff over their Jupyter notebooks
(https://github.com/langchain-ai/langchain/pull/12677/files), but
running into a bunch of syntax errors, the majority of which come from
our inability to recognize automagic.

If you run this in a cell:

```jupyter
pip install requests
```

Jupyter will automatically treat that as:

```jupyter
%pip install requests
```

We need to ignore cells that use these automagics, since the parser
doesn't understand them. (I guess we could support it in the parser, but
that seems much harder?). The good news is that AFAICT Jupyter doesn't
let you mix automagics with code, so by skipping these cells, we don't
miss out on analyzing any Python code.

## Test Plan

1. `cargo test`
2. Ran over LangChain and verified that there are no more errors
relating to `pip install` automagics.
2023-11-03 01:14:10 +00:00
Kar Petrosyan
2ff1afb15c Add initial flake8-trio rule (#8439)
## Summary

This pull request adds
[flake8-trio](https://github.com/Zac-HD/flake8-trio) support to ruff,
which is a very useful plugin for trio users to avoid very common
mistakes.

Part of https://github.com/astral-sh/ruff/issues/8451.

## Test Plan

Traditional rule testing, as [described in the
documentation](https://docs.astral.sh/ruff/contributing/#rule-testing-fixtures-and-snapshots).
2023-11-03 01:05:12 +00:00
Zanie Blue
7fa6ac976a Fix documentation for RuleTable (#8448) 2023-11-02 11:10:07 -05:00
Zanie Blue
7dd5137913 Fix ecosystem check bug where comment is no longer updated (#8446)
Instead, a second is posted
2023-11-02 10:49:57 -05:00
Zanie Blue
0d93fbb4a2 Only show ecosystem command used if options are non-default (#8435)
To save that precious character count
2023-11-02 08:53:33 -05:00
Dhruv Manilawala
d350ede992 Remove unicode flag from comparable (#8440)
## Summary

This PR removes the `unicode` flag from the string literal in
`ComparableExpr`. This flag isn't required as all strings are unicode in
Python 3 so `"foo" == u"foo"`.
2023-11-02 13:21:45 +05:30
851 changed files with 15170 additions and 6330 deletions

29
.github/release.yml vendored
View File

@@ -1,29 +0,0 @@
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes#configuring-automatically-generated-release-notes
changelog:
exclude:
labels:
- internal
- documentation
categories:
- title: Breaking Changes
labels:
- breaking
- title: Rules
labels:
- rule
- title: Settings
labels:
- configuration
- cli
- title: Bug Fixes
labels:
- bug
- title: Formatter
labels:
- formatter
- title: Preview
labels:
- preview
- title: Other Changes
labels:
- "*"

View File

@@ -184,7 +184,11 @@ jobs:
- cargo-test-linux
- determine_changes
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
if: github.event_name == 'pull_request'
# Ecosystem check needs linter and/or formatter changes.
if: github.event_name == 'pull_request' && ${{
needs.determine_changes.outputs.linter == 'true' ||
needs.determine_changes.outputs.formatter == 'true'
}}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4

View File

@@ -1,4 +1,4 @@
name: PR Check Comment
name: Ecosystem check comment
on:
workflow_run:
@@ -18,13 +18,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: dawidd6/action-download-artifact@v2
name: Download PR Number
name: Download pull request number
with:
name: pr-number
run_id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }}
if_no_artifact_found: ignore
- name: Extract PR Number
- name: Parse pull request number
id: pr-number
run: |
if [[ -f pr-number ]]
@@ -33,7 +33,7 @@ jobs:
fi
- uses: dawidd6/action-download-artifact@v2
name: "Download Ecosystem Result"
name: "Download ecosystem results"
id: download-ecosystem-result
if: steps.pr-number.outputs.pr-number
with:
@@ -44,10 +44,14 @@ jobs:
workflow_conclusion: completed
if_no_artifact_found: ignore
- name: Generate Comment
- name: Generate comment content
id: generate-comment
if: steps.download-ecosystem-result.outputs.found_artifact == 'true'
run: |
# Note this identifier is used to find the comment to update on
# subsequent runs
echo '<!-- generated-comment ecosystem -->' >> comment.txt
echo '## `ruff-ecosystem` results' >> comment.txt
cat pr/ecosystem/ecosystem-result >> comment.txt
echo "" >> comment.txt
@@ -56,14 +60,14 @@ jobs:
cat comment.txt >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT
- name: Find Comment
- name: Find existing comment
uses: peter-evans/find-comment@v2
if: steps.generate-comment.outcome == 'success'
id: find-comment
with:
issue-number: ${{ steps.pr-number.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: PR Check Results
body-includes: "<!-- generated-comment ecosystem -->"
- name: Create or update comment
if: steps.find-comment.outcome == 'success'

View File

@@ -13,12 +13,12 @@ exclude: |
repos:
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.12.1
rev: v0.15
hooks:
- id: validate-pyproject
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.16
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies:
@@ -26,16 +26,22 @@ repos:
- mdformat-admon
exclude: |
(?x)^(
docs/formatter/black.md
docs/formatter/black\.md
| docs/\w+\.md
)$
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.33.0
rev: v0.37.0
hooks:
- id: markdownlint-fix
exclude: |
(?x)^(
docs/formatter/black\.md
| docs/\w+\.md
)$
- repo: https://github.com/crate-ci/typos
rev: v1.14.12
rev: v1.16.22
hooks:
- id: typos
@@ -49,7 +55,7 @@ repos:
pass_filenames: false # This makes it a lot faster
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.3
rev: v0.1.4
hooks:
- id: ruff-format
- id: ruff
@@ -64,7 +70,7 @@ repos:
# Prettier
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0
rev: v3.0.3
hooks:
- id: prettier
types: [yaml]

View File

@@ -1,5 +1,115 @@
# Changelog
## 0.1.5
### Preview features
- \[`flake8-bandit`\] Implement `mako-templates` (`S702`) ([#8533](https://github.com/astral-sh/ruff/pull/8533))
- \[`flake8-trio`\] Implement `TRIO105` ([#8490](https://github.com/astral-sh/ruff/pull/8490))
- \[`flake8-trio`\] Implement `TRIO109` ([#8534](https://github.com/astral-sh/ruff/pull/8534))
- \[`flake8-trio`\] Implement `TRIO110` ([#8537](https://github.com/astral-sh/ruff/pull/8537))
- \[`flake8-trio`\] Implement `TRIO115` ([#8486](https://github.com/astral-sh/ruff/pull/8486))
- \[`refurb`\] Implement `type-none-comparison` (`FURB169`) ([#8487](https://github.com/astral-sh/ruff/pull/8487))
- Flag all comparisons against builtin types in `E721` ([#8491](https://github.com/astral-sh/ruff/pull/8491))
- Make `SIM118` fix as safe when the expression is a known dictionary ([#8525](https://github.com/astral-sh/ruff/pull/8525))
### Formatter
- Fix multiline lambda expression statement formatting ([#8466](https://github.com/astral-sh/ruff/pull/8466))
### CLI
- Add hidden `--extension` to override inference of source type from file extension ([#8373](https://github.com/astral-sh/ruff/pull/8373))
### Configuration
- Account for selector specificity when merging `extend_unsafe_fixes` and `override extend_safe_fixes` ([#8444](https://github.com/astral-sh/ruff/pull/8444))
- Add support for disabling cache with `RUFF_NO_CACHE` environment variable ([#8538](https://github.com/astral-sh/ruff/pull/8538))
### Bug fixes
- \[`E721`\] Flag comparisons to `memoryview` ([#8485](https://github.com/astral-sh/ruff/pull/8485))
- Allow collapsed-ellipsis bodies in other statements ([#8499](https://github.com/astral-sh/ruff/pull/8499))
- Avoid `D301` autofix for `u` prefixed strings ([#8495](https://github.com/astral-sh/ruff/pull/8495))
- Only flag `flake8-trio` rules when `trio` import is present ([#8550](https://github.com/astral-sh/ruff/pull/8550))
- Reject more syntactically invalid Python programs ([#8524](https://github.com/astral-sh/ruff/pull/8524))
- Avoid raising `TRIO115` violations for `trio.sleep(...)` calls with non-number values ([#8532](https://github.com/astral-sh/ruff/pull/8532))
- Fix `F841` false negative on assignment to multiple variables ([#8489](https://github.com/astral-sh/ruff/pull/8489))
### Documentation
- Fix link to isort `known-first-party` ([#8562](https://github.com/astral-sh/ruff/pull/8562))
- Add notes on fix safety to a few rules ([#8500](https://github.com/astral-sh/ruff/pull/8500))
- Add missing toml config tabs ([#8512](https://github.com/astral-sh/ruff/pull/8512))
- Add instructions for configuration of Emacs ([#8488](https://github.com/astral-sh/ruff/pull/8488))
- Improve detail link contrast in dark mode ([#8548](https://github.com/astral-sh/ruff/pull/8548))
- Fix typo in example ([#8506](https://github.com/astral-sh/ruff/pull/8506))
- Added tabs for configuration files in the documentation ([#8480](https://github.com/astral-sh/ruff/pull/8480))
- Recommend `project.requires-python` over `target-version` ([#8513](https://github.com/astral-sh/ruff/pull/8513))
- Add singleton escape hatch to `B008` documentation ([#8501](https://github.com/astral-sh/ruff/pull/8501))
- Fix tab configuration docs ([#8502](https://github.com/astral-sh/ruff/pull/8502))
## 0.1.4
### Preview features
- \[`flake8-trio`\] Implement `timeout-without-await` (`TRIO001`) ([#8439](https://github.com/astral-sh/ruff/pull/8439))
- \[`numpy`\] Implement NumPy 2.0 migration rule (`NPY200`) ([#7702](https://github.com/astral-sh/ruff/pull/7702))
- \[`pylint`\] Implement `bad-open-mode` (`W1501`) ([#8294](https://github.com/astral-sh/ruff/pull/8294))
- \[`pylint`\] Implement `import-outside-toplevel` (`C0415`) rule ([#5180](https://github.com/astral-sh/ruff/pull/5180))
- \[`pylint`\] Implement `useless-with-lock` (`W2101`) ([#8321](https://github.com/astral-sh/ruff/pull/8321))
- \[`pyupgrade`\] Implement `timeout-error-alias` (`UP041`) ([#8476](https://github.com/astral-sh/ruff/pull/8476))
- \[`refurb`\] Implement `isinstance-type-none` (`FURB168`) ([#8308](https://github.com/astral-sh/ruff/pull/8308))
- Detect confusable Unicode-to-Unicode units in `RUF001`, `RUF002`, and `RUF003` ([#4430](https://github.com/astral-sh/ruff/pull/4430))
- Add newline after module docstrings in preview style ([#8283](https://github.com/astral-sh/ruff/pull/8283))
### Formatter
- Add a note on line-too-long to the formatter docs ([#8314](https://github.com/astral-sh/ruff/pull/8314))
- Preserve trailing statement semicolons when using `fmt: skip` ([#8273](https://github.com/astral-sh/ruff/pull/8273))
- Preserve trailing semicolons when using `fmt: off` ([#8275](https://github.com/astral-sh/ruff/pull/8275))
- Avoid duplicating linter-formatter compatibility warnings ([#8292](https://github.com/astral-sh/ruff/pull/8292))
- Avoid inserting a newline after function docstrings ([#8375](https://github.com/astral-sh/ruff/pull/8375))
- Insert newline between docstring and following own line comment ([#8216](https://github.com/astral-sh/ruff/pull/8216))
- Split tuples in return positions by comma first ([#8280](https://github.com/astral-sh/ruff/pull/8280))
- Avoid treating byte strings as docstrings ([#8350](https://github.com/astral-sh/ruff/pull/8350))
- Add `--line-length` option to `format` command ([#8363](https://github.com/astral-sh/ruff/pull/8363))
- Avoid parenthesizing unsplittable because of comments ([#8431](https://github.com/astral-sh/ruff/pull/8431))
### CLI
- Add `--output-format` to `ruff rule` and `ruff linter` ([#8203](https://github.com/astral-sh/ruff/pull/8203))
### Bug fixes
- Respect `--force-exclude` in `lint.exclude` and `format.exclude` ([#8393](https://github.com/astral-sh/ruff/pull/8393))
- Respect `--extend-per-file-ignores` on the CLI ([#8329](https://github.com/astral-sh/ruff/pull/8329))
- Extend `bad-dunder-method-name` to permit `__index__` ([#8300](https://github.com/astral-sh/ruff/pull/8300))
- Fix panic with 8 in octal escape ([#8356](https://github.com/astral-sh/ruff/pull/8356))
- Avoid raising `D300` when both triple quote styles are present ([#8462](https://github.com/astral-sh/ruff/pull/8462))
- Consider unterminated f-strings in `FStringRanges` ([#8154](https://github.com/astral-sh/ruff/pull/8154))
- Avoid including literal `shell=True` for truthy, non-`True` diagnostics ([#8359](https://github.com/astral-sh/ruff/pull/8359))
- Avoid triggering single-element test for starred expressions ([#8433](https://github.com/astral-sh/ruff/pull/8433))
- Detect and ignore Jupyter automagics ([#8398](https://github.com/astral-sh/ruff/pull/8398))
- Fix invalid E231 error with f-strings ([#8369](https://github.com/astral-sh/ruff/pull/8369))
- Avoid triggering `NamedTuple` rewrite with starred annotation ([#8434](https://github.com/astral-sh/ruff/pull/8434))
- Avoid un-setting bracket flag in logical lines ([#8380](https://github.com/astral-sh/ruff/pull/8380))
- Place 'r' prefix before 'f' for raw format strings ([#8464](https://github.com/astral-sh/ruff/pull/8464))
- Remove trailing periods from NumPy 2.0 code actions ([#8475](https://github.com/astral-sh/ruff/pull/8475))
- Fix bug where `PLE1307` was raised when formatting `%c` with characters ([#8407](https://github.com/astral-sh/ruff/pull/8407))
- Remove unicode flag from comparable ([#8440](https://github.com/astral-sh/ruff/pull/8440))
- Improve B015 message ([#8295](https://github.com/astral-sh/ruff/pull/8295))
- Use `fixedOverflowWidgets` for playground popover ([#8458](https://github.com/astral-sh/ruff/pull/8458))
- Mark `byte_bounds` as a non-backwards-compatible NumPy 2.0 change ([#8474](https://github.com/astral-sh/ruff/pull/8474))
### Internals
- Add a dedicated cache directory per Ruff version ([#8333](https://github.com/astral-sh/ruff/pull/8333))
- Allow selective caching for `--fix` and `--diff` ([#8316](https://github.com/astral-sh/ruff/pull/8316))
- Improve performance of comment parsing ([#8193](https://github.com/astral-sh/ruff/pull/8193))
- Improve performance of string parsing ([#8227](https://github.com/astral-sh/ruff/pull/8227))
- Use a dedicated sort key for isort import sorting ([#7963](https://github.com/astral-sh/ruff/pull/7963))
## 0.1.3
This release includes a variety of improvements to the Ruff formatter, removing several known and

View File

@@ -72,7 +72,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
charlie.r.marsh@gmail.com.
<charlie.r.marsh@gmail.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

View File

@@ -315,9 +315,18 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
### Creating a new release
1. Update the version with `rg 0.0.269 --files-with-matches | xargs sed -i 's/0.0.269/0.0.270/g'`
1. Update `BREAKING_CHANGES.md`
1. Create a PR with the version and `BREAKING_CHANGES.md` updated
We use an experimental in-house tool for managing releases.
1. Install `rooster`: `pip install git+https://github.com/zanieb/rooster@main`
1. Run `rooster release`; this command will:
- Generate a changelog entry in `CHANGELOG.md`
- Update versions in `pyproject.toml` and `Cargo.toml`
- Update references to versions in the `README.md` and documentation
1. The changelog should then be editorialized for consistency
- Often labels will be missing from pull requests they will need to be manually organized into the proper section
- Changes should be edited to be user-facing descriptions, avoiding internal details
1. Highlight any breaking changes in `BREAKING_CHANGES.md`
1. Create a pull request with the changelog and version updates
1. Merge the PR
1. Run the release workflow with the version number (without starting `v`) as input. Make sure
main has your merged PR as last commit
@@ -330,7 +339,11 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
1. Attach artifacts to draft GitHub release
1. Trigger downstream repositories. This can fail non-catastrophically, as we can run any
downstream jobs manually if needed.
1. Create release notes in GitHub UI and promote from draft.
1. Publish the GitHub release
1. Open the draft release in the GitHub release section
1. Copy the changelog for the release into the GitHub release
- See previous releases for formatting of section headers
1. Generate the contributor list with `rooster contributors` and add to the release notes
1. If needed, [update the schemastore](https://github.com/charliermarsh/ruff/blob/main/scripts/update_schemastore.py)
1. If needed, update the `ruff-lsp` and `ruff-vscode` repositories.

82
Cargo.lock generated
View File

@@ -210,9 +210,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bstr"
@@ -383,7 +383,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -407,9 +407,9 @@ dependencies = [
[[package]]
name = "codspeed"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d680ccd1eedd2dd7c7a3649a78c7d06e0f16b191b30d81cc58e7bc906488d344"
checksum = "918b13a0f1a32460ab3bd5debd56b5a27a7071fa5ff5dfeb3a5cf291a85b174b"
dependencies = [
"colored",
"libc",
@@ -418,9 +418,9 @@ dependencies = [
[[package]]
name = "codspeed-criterion-compat"
version = "2.3.0"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58b48b6c8e890d7d4ad0ed85e9ab4949bf7023198c006000ef6338ba84cf5b71"
checksum = "c683c7fef2b873fbbdf4062782914c652309951244bf0bd362fe608b7d6f901c"
dependencies = [
"codspeed",
"colored",
@@ -608,7 +608,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -619,7 +619,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -810,7 +810,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.1.3"
version = "0.1.5"
dependencies = [
"anyhow",
"clap",
@@ -1129,7 +1129,7 @@ dependencies = [
"pmutil 0.6.1",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -1440,7 +1440,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.4.0",
"bitflags 2.4.1",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@@ -1707,7 +1707,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -2060,14 +2060,14 @@ dependencies = [
[[package]]
name = "ruff_cli"
version = "0.1.3"
version = "0.1.5"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
"argfile",
"assert_cmd",
"bincode",
"bitflags 2.4.0",
"bitflags 2.4.1",
"cachedir",
"chrono",
"clap",
@@ -2196,12 +2196,12 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.1.3"
version = "0.1.5"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.1",
"anyhow",
"bitflags 2.4.0",
"bitflags 2.4.1",
"chrono",
"clap",
"colored",
@@ -2267,7 +2267,7 @@ dependencies = [
"proc-macro2",
"quote",
"ruff_python_trivia",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -2293,7 +2293,7 @@ dependencies = [
name = "ruff_python_ast"
version = "0.0.0"
dependencies = [
"bitflags 2.4.0",
"bitflags 2.4.1",
"insta",
"is-macro",
"itertools 0.11.0",
@@ -2325,7 +2325,7 @@ name = "ruff_python_formatter"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags 2.4.0",
"bitflags 2.4.1",
"clap",
"countme",
"insta",
@@ -2369,7 +2369,7 @@ dependencies = [
name = "ruff_python_literal"
version = "0.0.0"
dependencies = [
"bitflags 2.4.0",
"bitflags 2.4.1",
"hexf-parse",
"is-macro",
"itertools 0.11.0",
@@ -2383,7 +2383,7 @@ name = "ruff_python_parser"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags 2.4.0",
"bitflags 2.4.1",
"insta",
"is-macro",
"itertools 0.11.0",
@@ -2413,7 +2413,7 @@ dependencies = [
name = "ruff_python_semantic"
version = "0.0.0"
dependencies = [
"bitflags 2.4.0",
"bitflags 2.4.1",
"is-macro",
"ruff_index",
"ruff_python_ast",
@@ -2447,7 +2447,7 @@ dependencies = [
[[package]]
name = "ruff_shrinking"
version = "0.1.3"
version = "0.1.5"
dependencies = [
"anyhow",
"clap",
@@ -2563,7 +2563,7 @@ version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
"bitflags 2.4.0",
"bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
@@ -2682,9 +2682,9 @@ dependencies = [
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.0"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30c9933e5689bd420dc6c87b7a1835701810cbc10cd86a26e4da45b73e6b1d78"
checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d"
dependencies = [
"js-sys",
"serde",
@@ -2699,7 +2699,7 @@ checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -2715,9 +2715,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@@ -2761,7 +2761,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -2856,7 +2856,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -2872,9 +2872,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.38"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
@@ -2961,7 +2961,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -2973,7 +2973,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
"test-case-core",
]
@@ -2994,7 +2994,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -3131,7 +3131,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -3349,7 +3349,7 @@ checksum = "3d8c6bba9b149ee82950daefc9623b32bb1dacbfb1890e352f6b887bd582adaf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
]
[[package]]
@@ -3443,7 +3443,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
"wasm-bindgen-shared",
]
@@ -3477,7 +3477,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.39",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@@ -13,7 +13,7 @@ license = "MIT"
[workspace.dependencies]
anyhow = { version = "1.0.69" }
bitflags = { version = "2.3.1" }
bitflags = { version = "2.4.1" }
chrono = { version = "0.4.31", default-features = false, features = ["clock"] }
clap = { version = "4.4.7", features = ["derive"] }
colored = { version = "2.0.0" }
@@ -35,14 +35,14 @@ regex = { version = "1.10.2" }
rustc-hash = { version = "1.1.0" }
schemars = { version = "0.8.15" }
serde = { version = "1.0.190", features = ["derive"] }
serde_json = { version = "1.0.107" }
serde_json = { version = "1.0.108" }
shellexpand = { version = "3.0.0" }
similar = { version = "2.3.0", features = ["inline"] }
smallvec = { version = "1.11.1" }
static_assertions = "1.1.0"
strum = { version = "0.25.0", features = ["strum_macros"] }
strum_macros = { version = "0.25.3" }
syn = { version = "2.0.38" }
syn = { version = "2.0.39" }
test-case = { version = "3.2.1" }
thiserror = { version = "1.0.50" }
toml = { version = "0.7.8" }

25
LICENSE
View File

@@ -1269,6 +1269,31 @@ are:
SOFTWARE.
"""
- flake8-trio, licensed as follows:
"""
MIT License
Copyright (c) 2022 Zac Hatfield-Dodds
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
- Pyright, licensed as follows:
"""
MIT License

View File

@@ -148,14 +148,14 @@ ruff format @arguments.txt # Format using an input file, treating its
Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff-pre-commit`](https://github.com/astral-sh/ruff-pre-commit):
```yaml
# Run the Ruff linter.
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.3
rev: v0.1.5
hooks:
# Run the Ruff linter.
# Run the linter.
- id: ruff
# Run the Ruff formatter.
args: [ --fix ]
# Run the formatter.
- id: ruff-format
```
@@ -314,6 +314,7 @@ quality tools, including:
- [flake8-super](https://pypi.org/project/flake8-super/)
- [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports/)
- [flake8-todos](https://pypi.org/project/flake8-todos/)
- [flake8-trio](https://pypi.org/project/flake8-trio/)
- [flake8-type-checking](https://pypi.org/project/flake8-type-checking/)
- [flake8-use-pathlib](https://pypi.org/project/flake8-use-pathlib/)
- [flynt](https://pypi.org/project/flynt/) ([#2102](https://github.com/astral-sh/ruff/issues/2102))
@@ -415,6 +416,7 @@ Ruff is used by a number of major open-source projects and companies, including:
- [PDM](https://github.com/pdm-project/pdm)
- [PaddlePaddle](https://github.com/PaddlePaddle/Paddle)
- [Pandas](https://github.com/pandas-dev/pandas)
- [Pillow](https://github.com/python-pillow/Pillow)
- [Poetry](https://github.com/python-poetry/poetry)
- [Polars](https://github.com/pola-rs/polars)
- [PostHog](https://github.com/PostHog/posthog)
@@ -423,6 +425,7 @@ Ruff is used by a number of major open-source projects and companies, including:
- [PyTorch](https://github.com/pytorch/pytorch)
- [Pydantic](https://github.com/pydantic/pydantic)
- [Pylint](https://github.com/PyCQA/pylint)
- [PyMC-Marketing](https://github.com/pymc-labs/pymc-marketing)
- [Reflex](https://github.com/reflex-dev/reflex)
- [Rippling](https://rippling.com)
- [Robyn](https://github.com/sansyrox/robyn)

View File

@@ -1,5 +1,6 @@
[files]
extend-exclude = ["resources", "snapshots"]
# https://github.com/crate-ci/typos/issues/868
extend-exclude = ["**/resources/**/*", "**/snapshots/**/*"]
[default.extend-words]
hel = "hel"

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.1.3"
version = "0.1.5"
description = """
Convert Flake8 configuration files to Ruff configuration files.
"""

View File

@@ -37,7 +37,7 @@ serde_json.workspace = true
url = "2.3.1"
ureq = "2.8.0"
criterion = { version = "0.5.1", default-features = false }
codspeed-criterion-compat = { version="2.3.0", default-features = false, optional = true}
codspeed-criterion-compat = { version="2.3.1", default-features = false, optional = true}
[dev-dependencies]
ruff_linter.path = "../ruff_linter"

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_cli"
version = "0.1.3"
version = "0.1.5"
publish = false
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -8,8 +8,8 @@ use ruff_linter::line_width::LineLength;
use ruff_linter::logging::LogLevel;
use ruff_linter::registry::Rule;
use ruff_linter::settings::types::{
FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion, SerializationFormat,
UnsafeFixes,
ExtensionPair, FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion,
SerializationFormat, UnsafeFixes,
};
use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser};
use ruff_workspace::configuration::{Configuration, RuleSelection};
@@ -278,7 +278,7 @@ pub struct CheckCommand {
#[arg(long, help_heading = "Rule configuration", hide = true)]
pub dummy_variable_rgx: Option<Regex>,
/// Disable cache reads.
#[arg(short, long, help_heading = "Miscellaneous")]
#[arg(short, long, env = "RUFF_NO_CACHE", help_heading = "Miscellaneous")]
pub no_cache: bool,
/// Ignore all configuration files.
#[arg(long, conflicts_with = "config", help_heading = "Miscellaneous")]
@@ -351,6 +351,9 @@ pub struct CheckCommand {
conflicts_with = "watch",
)]
pub show_settings: bool,
/// List of mappings from file extension to language (one of ["python", "ipynb", "pyi"]).
#[arg(long, value_delimiter = ',', hide = true)]
pub extension: Option<Vec<ExtensionPair>>,
/// Dev-only argument to show fixes
#[arg(long, hide = true)]
pub ecosystem_ci: bool,
@@ -374,7 +377,7 @@ pub struct FormatCommand {
pub config: Option<PathBuf>,
/// Disable cache reads.
#[arg(short, long, help_heading = "Miscellaneous")]
#[arg(short, long, env = "RUFF_NO_CACHE", help_heading = "Miscellaneous")]
pub no_cache: bool,
/// Path to the cache directory.
#[arg(long, env = "RUFF_CACHE_DIR", help_heading = "Miscellaneous")]
@@ -535,6 +538,7 @@ impl CheckCommand {
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
output_format: self.output_format,
show_fixes: resolve_bool_arg(self.show_fixes, self.no_show_fixes),
extension: self.extension,
},
)
}
@@ -647,6 +651,7 @@ pub struct CliOverrides {
pub force_exclude: Option<bool>,
pub output_format: Option<SerializationFormat>,
pub show_fixes: Option<bool>,
pub extension: Option<Vec<ExtensionPair>>,
}
impl ConfigurationTransformer for CliOverrides {
@@ -731,6 +736,9 @@ impl ConfigurationTransformer for CliOverrides {
if let Some(target_version) = &self.target_version {
config.target_version = Some(*target_version);
}
if let Some(extension) = &self.extension {
config.lint.extension = Some(extension.clone().into_iter().collect());
}
config
}

View File

@@ -30,6 +30,7 @@ use crate::diagnostics::Diagnostics;
use crate::panic::catch_unwind;
/// Run the linter over a collection of files.
#[allow(clippy::too_many_arguments)]
pub(crate) fn check(
files: &[PathBuf],
pyproject_config: &PyprojectConfig,
@@ -184,6 +185,7 @@ pub(crate) fn check(
/// Wraps [`lint_path`](crate::diagnostics::lint_path) in a [`catch_unwind`](std::panic::catch_unwind) and emits
/// a diagnostic if the linting the file panics.
#[allow(clippy::too_many_arguments)]
fn lint_path(
path: &Path,
package: Option<&Path>,

View File

@@ -17,13 +17,13 @@ use ruff_linter::logging::DisplayParseError;
use ruff_linter::message::Message;
use ruff_linter::pyproject_toml::lint_pyproject_toml;
use ruff_linter::registry::AsRule;
use ruff_linter::settings::types::UnsafeFixes;
use ruff_linter::settings::types::{ExtensionMapping, UnsafeFixes};
use ruff_linter::settings::{flags, LinterSettings};
use ruff_linter::source_kind::{SourceError, SourceKind};
use ruff_linter::{fs, IOError, SyntaxError};
use ruff_notebook::{Notebook, NotebookError, NotebookIndex};
use ruff_python_ast::imports::ImportMap;
use ruff_python_ast::{SourceType, TomlSourceType};
use ruff_python_ast::{PySourceType, SourceType, TomlSourceType};
use ruff_source_file::{LineIndex, SourceCode, SourceFileBuilder};
use ruff_text_size::{TextRange, TextSize};
use ruff_workspace::Settings;
@@ -177,6 +177,11 @@ impl AddAssign for FixMap {
}
}
fn override_source_type(path: Option<&Path>, extension: &ExtensionMapping) -> Option<PySourceType> {
let ext = path?.extension()?.to_str()?;
extension.get(ext).map(PySourceType::from)
}
/// Lint the source code at the given `Path`.
pub(crate) fn lint_path(
path: &Path,
@@ -221,31 +226,35 @@ pub(crate) fn lint_path(
debug!("Checking: {}", path.display());
let source_type = match SourceType::from(path) {
SourceType::Toml(TomlSourceType::Pyproject) => {
let messages = if settings
.rules
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
{
let contents = match std::fs::read_to_string(path).map_err(SourceError::from) {
Ok(contents) => contents,
Err(err) => {
return Ok(Diagnostics::from_source_error(&err, Some(path), settings));
}
let source_type = match override_source_type(Some(path), &settings.extension) {
Some(source_type) => source_type,
None => match SourceType::from(path) {
SourceType::Toml(TomlSourceType::Pyproject) => {
let messages = if settings
.rules
.iter_enabled()
.any(|rule_code| rule_code.lint_source().is_pyproject_toml())
{
let contents = match std::fs::read_to_string(path).map_err(SourceError::from) {
Ok(contents) => contents,
Err(err) => {
return Ok(Diagnostics::from_source_error(&err, Some(path), settings));
}
};
let source_file =
SourceFileBuilder::new(path.to_string_lossy(), contents).finish();
lint_pyproject_toml(source_file, settings)
} else {
vec![]
};
let source_file = SourceFileBuilder::new(path.to_string_lossy(), contents).finish();
lint_pyproject_toml(source_file, settings)
} else {
vec![]
};
return Ok(Diagnostics {
messages,
..Diagnostics::default()
});
}
SourceType::Toml(_) => return Ok(Diagnostics::default()),
SourceType::Python(source_type) => source_type,
return Ok(Diagnostics {
messages,
..Diagnostics::default()
});
}
SourceType::Toml(_) => return Ok(Diagnostics::default()),
SourceType::Python(source_type) => source_type,
},
};
// Extract the sources from the file.
@@ -370,8 +379,15 @@ pub(crate) fn lint_stdin(
fix_mode: flags::FixMode,
) -> Result<Diagnostics> {
// TODO(charlie): Support `pyproject.toml`.
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
return Ok(Diagnostics::default());
let source_type = if let Some(source_type) =
override_source_type(path, &settings.linter.extension)
{
source_type
} else {
let SourceType::Python(source_type) = path.map(SourceType::from).unwrap_or_default() else {
return Ok(Diagnostics::default());
};
source_type
};
// Extract the sources from the file.

View File

@@ -320,6 +320,119 @@ fn stdin_fix_jupyter() {
Found 2 errors (2 fixed, 0 remaining).
"###);
}
#[test]
fn stdin_override_parser_ipynb() {
let args = ["--extension", "py:ipynb", "--stdin-filename", "Jupyter.py"];
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(args)
.pass_stdin(r#"{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "dccc687c-96e2-4604-b957-a8a89b5bec06",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
},
{
"cell_type": "markdown",
"id": "19e1b029-f516-4662-a9b9-623b93edac1a",
"metadata": {},
"source": [
"Foo"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "cdce7b92-b0fb-4c02-86f6-e233b26fa84f",
"metadata": {},
"outputs": [],
"source": [
"import sys"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "e40b33d2-7fe4-46c5-bdf0-8802f3052565",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n"
]
}
],
"source": [
"print(1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a1899bc8-d46f-4ec0-b1d1-e1ca0f04bf60",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}"#), @r###"
success: false
exit_code: 1
----- stdout -----
Jupyter.py:cell 1:1:8: F401 [*] `os` imported but unused
Jupyter.py:cell 3:1:8: F401 [*] `sys` imported but unused
Found 2 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
"###);
}
#[test]
fn stdin_override_parser_py() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--extension", "ipynb:python", "--stdin-filename", "F401.ipynb"])
.pass_stdin("import os\n"), @r###"
success: false
exit_code: 1
----- stdout -----
F401.ipynb:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
"###);
}
#[test]
fn stdin_fix_when_not_fixable_should_still_print_contents() {
@@ -1407,3 +1520,47 @@ extend-safe-fixes = ["UP034"]
Ok(())
}
#[test]
fn check_extend_unsafe_fixes_conflict_with_extend_safe_fixes_by_specificity() -> Result<()> {
// Adding a rule to one option with a more specific selector should override the other option
let tempdir = TempDir::new()?;
let ruff_toml = tempdir.path().join("ruff.toml");
fs::write(
&ruff_toml,
r#"
target-version = "py310"
[lint]
extend-unsafe-fixes = ["UP", "UP034"]
extend-safe-fixes = ["UP03"]
"#,
)?;
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(["check", "--config"])
.arg(&ruff_toml)
.arg("-")
.args([
"--output-format",
"text",
"--no-cache",
"--select",
"F601,UP018,UP034,UP038",
])
.pass_stdin("x = {'a': 1, 'a': 1}\nprint(('foo'))\nprint(str('foo'))\nisinstance(x, (int, str))\n"),
@r###"
success: false
exit_code: 1
----- stdout -----
-:1:14: F601 Dictionary key literal `'a'` repeated
-:2:7: UP034 Avoid extraneous parentheses
-:3:7: UP018 Unnecessary `str` call (rewrite as a literal)
-:4:1: UP038 [*] Use `X | Y` in `isinstance` call instead of `(X, Y)`
Found 4 errors.
[*] 1 fixable with the `--fix` option (3 hidden fixes can be enabled with the `--unsafe-fixes` option).
----- stderr -----
"###);
Ok(())
}

View File

@@ -3,6 +3,7 @@
//! Used for <https://docs.astral.sh/ruff/settings/>.
use std::fmt::Write;
use ruff_python_trivia::textwrap;
use ruff_workspace::options::Options;
use ruff_workspace::options_base::{OptionField, OptionSet, OptionsMetadata, Visit};
@@ -125,22 +126,57 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parent_set:
output.push('\n');
output.push_str(&format!("**Type**: `{}`\n", field.value_type));
output.push('\n');
output.push_str(&format!(
"**Example usage**:\n\n```toml\n[tool.ruff{}]\n{}\n```\n",
if let Some(set_name) = parent_set.name() {
if set_name == "format" {
String::from(".format")
} else {
format!(".lint.{set_name}")
}
} else {
String::new()
},
field.example
output.push_str("**Example usage**:\n\n");
output.push_str(&format_tab(
"pyproject.toml",
&format_header(parent_set, ConfigurationFile::PyprojectToml),
field.example,
));
output.push_str(&format_tab(
"ruff.toml",
&format_header(parent_set, ConfigurationFile::RuffToml),
field.example,
));
output.push('\n');
}
fn format_tab(tab_name: &str, header: &str, content: &str) -> String {
format!(
"=== \"{}\"\n\n ```toml\n {}\n{}\n ```\n",
tab_name,
header,
textwrap::indent(content, " ")
)
}
fn format_header(parent_set: &Set, configuration: ConfigurationFile) -> String {
let fmt = if let Some(set_name) = parent_set.name() {
if set_name == "format" {
String::from(".format")
} else {
format!(".lint.{set_name}")
}
} else {
String::new()
};
match configuration {
ConfigurationFile::PyprojectToml => format!("[tool.ruff{fmt}]"),
ConfigurationFile::RuffToml => {
if fmt.is_empty() {
String::new()
} else {
format!("[{}]", fmt.strip_prefix('.').unwrap())
}
}
}
}
#[derive(Debug, Copy, Clone)]
enum ConfigurationFile {
PyprojectToml,
RuffToml,
}
#[derive(Default)]
struct CollectOptionsVisitor {
groups: Vec<(String, OptionSet)>,

View File

@@ -12,7 +12,7 @@ use crate::edit::Edit;
pub enum Applicability {
/// The fix is unsafe and should only be displayed for manual application by the user.
/// The fix is likely to be incorrect or the resulting code may have invalid syntax.
Display,
DisplayOnly,
/// The fix is unsafe and should only be applied with user opt-in.
/// The fix may be what the user intended, but it is uncertain; the resulting code will have valid syntax.
@@ -87,22 +87,46 @@ impl Fix {
}
}
/// Create a new [`Fix`] that should only [display](Applicability::Display) and not apply from an [`Edit`] element .
pub fn display_edit(edit: Edit) -> Self {
/// Create a new [`Fix`] that should only [display](Applicability::DisplayOnly) and not apply from an [`Edit`] element .
pub fn display_only_edit(edit: Edit) -> Self {
Self {
edits: vec![edit],
applicability: Applicability::Display,
applicability: Applicability::DisplayOnly,
isolation_level: IsolationLevel::default(),
}
}
/// Create a new [`Fix`] that should only [display](Applicability::Display) and not apply from multiple [`Edit`] elements.
pub fn display_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
/// Create a new [`Fix`] that should only [display](Applicability::DisplayOnly) and not apply from multiple [`Edit`] elements.
pub fn display_only_edits(edit: Edit, rest: impl IntoIterator<Item = Edit>) -> Self {
let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
edits.sort_by_key(|edit| (edit.start(), edit.end()));
Self {
edits,
applicability: Applicability::Display,
applicability: Applicability::DisplayOnly,
isolation_level: IsolationLevel::default(),
}
}
/// Create a new [`Fix`] with the specified [`Applicability`] to apply an [`Edit`] element.
pub fn applicable_edit(edit: Edit, applicability: Applicability) -> Self {
Self {
edits: vec![edit],
applicability,
isolation_level: IsolationLevel::default(),
}
}
/// Create a new [`Fix`] with the specified [`Applicability`] to apply multiple [`Edit`] elements.
pub fn applicable_edits(
edit: Edit,
rest: impl IntoIterator<Item = Edit>,
applicability: Applicability,
) -> Self {
let mut edits: Vec<Edit> = std::iter::once(edit).chain(rest).collect();
edits.sort_by_key(|edit| (edit.start(), edit.end()));
Self {
edits,
applicability,
isolation_level: IsolationLevel::default(),
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
version = "0.1.3"
version = "0.1.5"
publish = false
authors = { workspace = true }
edition = { workspace = true }

View File

@@ -0,0 +1,9 @@
from mako.template import Template
from mako import template
import mako
Template("hello")
mako.template.Template("hern")
template.Template("hern")

View File

@@ -58,3 +58,33 @@ def f_fix_indentation_check(foo):
# Report these, but don't fix them
if foo: raise RuntimeError("This is an example exception")
if foo: x = 1; raise RuntimeError("This is an example exception")
def f_triple_quoted_string():
raise RuntimeError(f"""This is an {"example"} exception""")
def f_multi_line_string():
raise RuntimeError(
"first"
"second"
)
def f_multi_line_string2():
raise RuntimeError(
"This is an {example} exception".format(
example="example"
)
)
def f_multi_line_string2():
raise RuntimeError(
(
"This is an "
"{example} exception"
).format(
example="example"
)
)

View File

@@ -1,3 +1,5 @@
obj = {}
key in obj.keys() # SIM118
key not in obj.keys() # SIM118

View File

@@ -0,0 +1,18 @@
import trio
async def foo():
with trio.fail_after():
...
async def foo():
with trio.fail_at():
await ...
async def foo():
with trio.move_on_after():
...
async def foo():
with trio.move_at():
await ...

View File

@@ -0,0 +1,64 @@
import trio
async def func() -> None:
trio.run(foo) # OK, not async
# OK
await trio.aclose_forcefully(foo)
await trio.open_file(foo)
await trio.open_ssl_over_tcp_listeners(foo, foo)
await trio.open_ssl_over_tcp_stream(foo, foo)
await trio.open_tcp_listeners(foo)
await trio.open_tcp_stream(foo, foo)
await trio.open_unix_socket(foo)
await trio.run_process(foo)
await trio.sleep(5)
await trio.sleep_until(5)
await trio.lowlevel.cancel_shielded_checkpoint()
await trio.lowlevel.checkpoint()
await trio.lowlevel.checkpoint_if_cancelled()
await trio.lowlevel.open_process(foo)
await trio.lowlevel.permanently_detach_coroutine_object(foo)
await trio.lowlevel.reattach_detached_coroutine_object(foo, foo)
await trio.lowlevel.temporarily_detach_coroutine_object(foo)
await trio.lowlevel.wait_readable(foo)
await trio.lowlevel.wait_task_rescheduled(foo)
await trio.lowlevel.wait_writable(foo)
# TRIO105
trio.aclose_forcefully(foo)
trio.open_file(foo)
trio.open_ssl_over_tcp_listeners(foo, foo)
trio.open_ssl_over_tcp_stream(foo, foo)
trio.open_tcp_listeners(foo)
trio.open_tcp_stream(foo, foo)
trio.open_unix_socket(foo)
trio.run_process(foo)
trio.serve_listeners(foo, foo)
trio.serve_ssl_over_tcp(foo, foo, foo)
trio.serve_tcp(foo, foo)
trio.sleep(foo)
trio.sleep_forever()
trio.sleep_until(foo)
trio.lowlevel.cancel_shielded_checkpoint()
trio.lowlevel.checkpoint()
trio.lowlevel.checkpoint_if_cancelled()
trio.lowlevel.open_process()
trio.lowlevel.permanently_detach_coroutine_object(foo)
trio.lowlevel.reattach_detached_coroutine_object(foo, foo)
trio.lowlevel.temporarily_detach_coroutine_object(foo)
trio.lowlevel.wait_readable(foo)
trio.lowlevel.wait_task_rescheduled(foo)
trio.lowlevel.wait_writable(foo)
async with await trio.open_file(foo): # Ok
pass
async with trio.open_file(foo): # TRIO105
pass
def func() -> None:
# TRIO105 (without fix)
trio.open_file(foo)

View File

@@ -0,0 +1,13 @@
import trio
async def func():
...
async def func(timeout):
...
async def func(timeout=10):
...

View File

@@ -0,0 +1,16 @@
import trio
async def func():
while True:
await trio.sleep(10)
async def func():
while True:
await trio.sleep_until(10)
async def func():
while True:
trio.sleep(10)

View File

@@ -0,0 +1,28 @@
import trio
from trio import sleep
async def func():
await trio.sleep(0) # TRIO115
await trio.sleep(1) # OK
await trio.sleep(0, 1) # OK
await trio.sleep(...) # OK
await trio.sleep() # OK
trio.sleep(0) # TRIO115
foo = 0
trio.sleep(foo) # TRIO115
trio.sleep(1) # OK
time.sleep(0) # OK
sleep(0) # TRIO115
bar = "bar"
trio.sleep(bar)
trio.sleep(0) # TRIO115
def func():
trio.run(trio.sleep(0)) # TRIO115

View File

@@ -0,0 +1,11 @@
from __future__ import annotations
from collections.abc import Sequence
class MyBaseClass:
pass
class Foo(MyBaseClass):
foo: Sequence

View File

@@ -0,0 +1,9 @@
from __future__ import annotations
from collections.abc import Sequence
from module.direct import MyBaseClass
class Foo(MyBaseClass):
foo: Sequence

View File

@@ -0,0 +1,7 @@
from __future__ import annotations
from collections.abc import Sequence
class Foo(MyBaseClass):
foo: Sequence

View File

@@ -0,0 +1,11 @@
from __future__ import annotations
from collections.abc import Sequence # TCH003
class MyBaseClass:
pass
class Foo(MyBaseClass):
foo: Sequence

View File

@@ -0,0 +1,106 @@
def func():
import numpy as np
np.add_docstring
np.add_newdoc
np.add_newdoc_ufunc
np.asfarray([1,2,3])
np.byte_bounds(np.array([1,2,3]))
np.cast
np.cfloat(12+34j)
np.clongfloat(12+34j)
np.compat
np.complex_(12+34j)
np.DataSource
np.deprecate
np.deprecate_with_doc
np.disp(10)
np.fastCopyAndTranspose
np.find_common_type
np.get_array_wrap
np.float_
np.geterrobj
np.Inf
np.Infinity
np.infty
np.issctype
np.issubclass_(np.int32, np.integer)
np.issubsctype
np.mat
np.maximum_sctype
np.NaN
np.nbytes[np.int64]
np.NINF
np.NZERO
np.longcomplex(12+34j)
np.longfloat(12+34j)
np.lookfor
np.obj2sctype(int)
np.PINF
np.PZERO
np.recfromcsv
np.recfromtxt
np.round_(12.34)
np.safe_eval
np.sctype2char
np.sctypes
np.seterrobj
np.set_numeric_ops
np.set_string_function
np.singlecomplex(12+1j)
np.string_("asdf")
np.source
np.tracemalloc_domain
np.unicode_("asf")
np.who()

View File

@@ -69,3 +69,5 @@ while 1:
#: E701:2:3
a = \
5;
#:
with x(y) as z: ...

View File

@@ -4,6 +4,9 @@ if type(res) == type(42):
#: E721
if type(res) != type(""):
pass
#: E721
if type(res) == memoryview:
pass
#: Okay
import types
@@ -47,6 +50,14 @@ if isinstance(res, str):
pass
if isinstance(res, types.MethodType):
pass
if isinstance(res, memoryview):
pass
#: Okay
if type(res) is type:
pass
#: E721
if type(res) == type:
pass
#: Okay
def func_histype(a, b, c):
pass

View File

@@ -8,3 +8,19 @@ def ends_in_quote():
def contains_quote():
'Sum"\\mary.'
# OK
def contains_triples(t):
"""('''|\""")"""
# OK
def contains_triples(t):
'''(\'''|""")'''
# TODO: here should raise D300 for using dobule quotes instead,
# because escaped double quote does allow us.
def contains_triples(t):
'''(\""")'''

View File

@@ -31,3 +31,7 @@ def make_unique_pod_id(pod_id: str) -> str | None:
:param pod_id: requested pod name
:return: ``str`` valid Pod name of appropriate length
"""
def shouldnt_add_raw_here2():
u"Sum\\mary."

View File

@@ -1,5 +1,5 @@
def f(tup):
x, y = tup # this does NOT trigger F841
x, y = tup
def f():
@@ -7,17 +7,17 @@ def f():
def f():
(x, y) = coords = 1, 2 # this does NOT trigger F841
(x, y) = coords = 1, 2
if x > 1:
print(coords)
def f():
(x, y) = coords = 1, 2 # this triggers F841 on coords
(x, y) = coords = 1, 2
def f():
coords = (x, y) = 1, 2 # this triggers F841 on coords
coords = (x, y) = 1, 2
def f():

View File

@@ -0,0 +1,32 @@
"""Test fix for issue #8441.
Ref: https://github.com/astral-sh/ruff/issues/8441
"""
def foo():
...
def bar():
a = foo()
b, c = foo()
def baz():
d, _e = foo()
print(d)
def qux():
f, _ = foo()
print(f)
def quux():
g, h = foo()
print(g, h)
def quuz():
_i, _j = foo()

View File

@@ -0,0 +1,68 @@
import asyncio, socket
# These should be fixed
try:
pass
except asyncio.TimeoutError:
pass
try:
pass
except socket.timeout:
pass
# Should NOT be in parentheses when replaced
try:
pass
except (asyncio.TimeoutError,):
pass
try:
pass
except (socket.timeout,):
pass
try:
pass
except (asyncio.TimeoutError, socket.timeout,):
pass
# Should be kept in parentheses (because multiple)
try:
pass
except (asyncio.TimeoutError, socket.timeout, KeyError, TimeoutError):
pass
# First should change, second should not
from .mmap import error
try:
pass
except (asyncio.TimeoutError, error):
pass
# These should not change
from foo import error
try:
pass
except (TimeoutError, error):
pass
try:
pass
except:
pass
try:
pass
except TimeoutError:
pass
try:
pass
except (TimeoutError, KeyError):
pass

View File

@@ -123,6 +123,15 @@ def yes_six(x: list):
x.append(2)
if True:
# FURB113
nums.append(1)
# comment
nums.append(2)
# comment
nums.append(3)
# Non-errors.
nums.append(1)

View File

@@ -43,6 +43,12 @@ def yes_four(x: Dict[int, str]):
del x[:]
def yes_five(x: Dict[int, str]):
# FURB131
del x[:]
x = 1
# these should not
del names["key"]

View File

@@ -0,0 +1,67 @@
foo = None
# Error.
type(foo) is type(None)
type(None) is type(foo)
type(None) is type(None)
type(foo) is not type(None)
type(None) is not type(foo)
type(None) is not type(None)
type(foo) == type(None)
type(None) == type(foo)
type(None) == type(None)
type(foo) != type(None)
type(None) != type(foo)
type(None) != type(None)
# Ok.
foo is None
foo is not None
None is foo
None is not foo
None is None
None is not None
foo is type(None)
type(foo) is None
type(None) is None
foo is not type(None)
type(foo) is not None
type(None) is not None
foo == type(None)
type(foo) == None
type(None) == None
foo != type(None)
type(foo) != None
type(None) != None
type(foo) > type(None)

View File

@@ -45,3 +45,11 @@ x = f"string { # And here's a comment with an unusual parenthesis:
# And here's a comment with a greek alpha:
foo # And here's a comment with an unusual punctuation mark:
}"
# At runtime the attribute will be stored as Greek small letter mu instead of
# micro sign because of PEP 3131's NFKC normalization
class Labware:
µL = 1.5
assert getattr(Labware(), "µL") == 1.5

View File

@@ -15,8 +15,8 @@ use crate::rules::{
flake8_comprehensions, flake8_datetimez, flake8_debugger, flake8_django,
flake8_future_annotations, flake8_gettext, flake8_implicit_str_concat, flake8_logging,
flake8_logging_format, flake8_pie, flake8_print, flake8_pyi, flake8_pytest_style, flake8_self,
flake8_simplify, flake8_tidy_imports, flake8_use_pathlib, flynt, numpy, pandas_vet,
pep8_naming, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff,
flake8_simplify, flake8_tidy_imports, flake8_trio, flake8_use_pathlib, flynt, numpy,
pandas_vet, pep8_naming, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff,
};
use crate::settings::types::PythonVersion;
@@ -158,6 +158,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::NumpyDeprecatedFunction) {
numpy::rules::deprecated_function(checker, expr);
}
if checker.enabled(Rule::Numpy2Deprecation) {
numpy::rules::numpy_2_0_deprecation(checker, expr);
}
if checker.enabled(Rule::CollectionsNamedTuple) {
flake8_pyi::rules::collections_named_tuple(checker, expr);
}
@@ -314,6 +317,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::NumpyDeprecatedFunction) {
numpy::rules::deprecated_function(checker, expr);
}
if checker.enabled(Rule::Numpy2Deprecation) {
numpy::rules::numpy_2_0_deprecation(checker, expr);
}
if checker.enabled(Rule::DeprecatedMockImport) {
pyupgrade::rules::deprecated_mock_attribute(checker, expr);
}
@@ -460,6 +466,11 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::OSErrorAlias) {
pyupgrade::rules::os_error_alias_call(checker, func);
}
if checker.enabled(Rule::TimeoutErrorAlias) {
if checker.settings.target_version >= PythonVersion::Py310 {
pyupgrade::rules::timeout_error_alias_call(checker, func);
}
}
if checker.enabled(Rule::NonPEP604Isinstance) {
if checker.settings.target_version >= PythonVersion::Py310 {
pyupgrade::rules::use_pep604_isinstance(checker, expr, func, args);
@@ -560,6 +571,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::Jinja2AutoescapeFalse) {
flake8_bandit::rules::jinja2_autoescape_false(checker, call);
}
if checker.enabled(Rule::MakoTemplates) {
flake8_bandit::rules::mako_templates(checker, call);
}
if checker.enabled(Rule::HardcodedPasswordFuncArg) {
flake8_bandit::rules::hardcoded_password_func_arg(checker, keywords);
}
@@ -915,6 +929,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::ImplicitCwd) {
refurb::rules::no_implicit_cwd(checker, call);
}
if checker.enabled(Rule::TrioSyncCall) {
flake8_trio::rules::sync_call(checker, call);
}
if checker.enabled(Rule::TrioZeroSleepCall) {
flake8_trio::rules::zero_sleep_call(checker, call);
}
}
Expr::Dict(
dict @ ast::ExprDict {
@@ -1224,6 +1244,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
comparators,
);
}
if checker.enabled(Rule::TypeNoneComparison) {
refurb::rules::type_none_comparison(checker, compare);
}
if checker.enabled(Rule::SingleItemMembershipTest) {
refurb::rules::single_item_membership_test(checker, expr, left, ops, comparators);
}

View File

@@ -12,8 +12,8 @@ use crate::rules::{
airflow, flake8_bandit, flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_debugger,
flake8_django, flake8_errmsg, flake8_import_conventions, flake8_pie, flake8_pyi,
flake8_pytest_style, flake8_raise, flake8_return, flake8_simplify, flake8_slots,
flake8_tidy_imports, flake8_type_checking, mccabe, pandas_vet, pep8_naming, perflint,
pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops,
flake8_tidy_imports, flake8_trio, flake8_type_checking, mccabe, pandas_vet, pep8_naming,
perflint, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops,
};
use crate::settings::types::PythonVersion;
@@ -356,6 +356,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_builtins::rules::builtin_variable_shadowing(checker, name, name.range());
}
}
if checker.enabled(Rule::TrioAsyncFunctionWithTimeout) {
flake8_trio::rules::async_function_with_timeout(checker, function_def);
}
#[cfg(feature = "unreachable-code")]
if checker.enabled(Rule::UnreachableCode) {
checker
@@ -1006,6 +1009,13 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pyupgrade::rules::os_error_alias_raise(checker, item);
}
}
if checker.enabled(Rule::TimeoutErrorAlias) {
if checker.settings.target_version >= PythonVersion::Py310 {
if let Some(item) = exc {
pyupgrade::rules::timeout_error_alias_raise(checker, item);
}
}
}
if checker.enabled(Rule::RaiseVanillaClass) {
if let Some(expr) = exc {
tryceratops::rules::raise_vanilla_class(checker, expr);
@@ -1195,8 +1205,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::UselessWithLock) {
pylint::rules::useless_with_lock(checker, with_stmt);
}
if checker.enabled(Rule::TrioTimeoutWithoutAwait) {
flake8_trio::rules::timeout_without_await(checker, with_stmt, items);
}
}
Stmt::While(ast::StmtWhile { body, orelse, .. }) => {
Stmt::While(while_stmt @ ast::StmtWhile { body, orelse, .. }) => {
if checker.enabled(Rule::FunctionUsesLoopVariable) {
flake8_bugbear::rules::function_uses_loop_variable(checker, &Node::Stmt(stmt));
}
@@ -1206,6 +1219,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::TryExceptInLoop) {
perflint::rules::try_except_in_loop(checker, body);
}
if checker.enabled(Rule::TrioUnneededSleep) {
flake8_trio::rules::unneeded_sleep(checker, while_stmt);
}
}
Stmt::For(
for_stmt @ ast::StmtFor {
@@ -1301,6 +1317,11 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::OSErrorAlias) {
pyupgrade::rules::os_error_alias_handlers(checker, handlers);
}
if checker.enabled(Rule::TimeoutErrorAlias) {
if checker.settings.target_version >= PythonVersion::Py310 {
pyupgrade::rules::timeout_error_alias_handlers(checker, handlers);
}
}
if checker.enabled(Rule::PytestAssertInExcept) {
flake8_pytest_style::rules::assert_in_exception_handler(checker, handlers);
}

View File

@@ -1415,7 +1415,7 @@ impl<'a> Checker<'a> {
// subsequent nodes are evaluated in the inner scope.
//
// For example, given:
// ```py
// ```python
// class A:
// T = range(10)
//
@@ -1423,7 +1423,7 @@ impl<'a> Checker<'a> {
// ```
//
// Conceptually, this is compiled as:
// ```py
// ```python
// class A:
// T = range(10)
//

View File

@@ -290,6 +290,13 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Async, "101") => (RuleGroup::Stable, rules::flake8_async::rules::OpenSleepOrSubprocessInAsyncFunction),
(Flake8Async, "102") => (RuleGroup::Stable, rules::flake8_async::rules::BlockingOsCallInAsyncFunction),
// flake8-trio
(Flake8Trio, "100") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioTimeoutWithoutAwait),
(Flake8Trio, "105") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioSyncCall),
(Flake8Trio, "109") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioAsyncFunctionWithTimeout),
(Flake8Trio, "110") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioUnneededSleep),
(Flake8Trio, "115") => (RuleGroup::Preview, rules::flake8_trio::rules::TrioZeroSleepCall),
// flake8-builtins
(Flake8Builtins, "001") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinVariableShadowing),
(Flake8Builtins, "002") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinArgumentShadowing),
@@ -497,6 +504,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pyupgrade, "038") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP604Isinstance),
(Pyupgrade, "039") => (RuleGroup::Stable, rules::pyupgrade::rules::UnnecessaryClassParentheses),
(Pyupgrade, "040") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695TypeAlias),
(Pyupgrade, "041") => (RuleGroup::Preview, rules::pyupgrade::rules::TimeoutErrorAlias),
// pydocstyle
(Pydocstyle, "100") => (RuleGroup::Stable, rules::pydocstyle::rules::UndocumentedPublicModule),
@@ -623,6 +631,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Bandit, "609") => (RuleGroup::Stable, rules::flake8_bandit::rules::UnixCommandWildcardInjection),
(Flake8Bandit, "612") => (RuleGroup::Stable, rules::flake8_bandit::rules::LoggingConfigInsecureListen),
(Flake8Bandit, "701") => (RuleGroup::Stable, rules::flake8_bandit::rules::Jinja2AutoescapeFalse),
(Flake8Bandit, "702") => (RuleGroup::Preview, rules::flake8_bandit::rules::MakoTemplates),
// flake8-boolean-trap
(Flake8BooleanTrap, "001") => (RuleGroup::Stable, rules::flake8_boolean_trap::rules::BooleanTypeHintPositionalArgument),
@@ -856,6 +865,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Numpy, "001") => (RuleGroup::Stable, rules::numpy::rules::NumpyDeprecatedTypeAlias),
(Numpy, "002") => (RuleGroup::Stable, rules::numpy::rules::NumpyLegacyRandom),
(Numpy, "003") => (RuleGroup::Stable, rules::numpy::rules::NumpyDeprecatedFunction),
(Numpy, "201") => (RuleGroup::Preview, rules::numpy::rules::Numpy2Deprecation),
// ruff
(Ruff, "001") => (RuleGroup::Stable, rules::ruff::rules::AmbiguousUnicodeCharacterString),
@@ -939,6 +949,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy),
(Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate),
(Refurb, "168") => (RuleGroup::Preview, rules::refurb::rules::IsinstanceTypeNone),
(Refurb, "169") => (RuleGroup::Preview, rules::refurb::rules::TypeNoneComparison),
(Refurb, "171") => (RuleGroup::Preview, rules::refurb::rules::SingleItemMembershipTest),
(Refurb, "177") => (RuleGroup::Preview, rules::refurb::rules::ImplicitCwd),

View File

@@ -189,7 +189,7 @@ impl<'a> Insertion<'a> {
Tok::NonLogicalNewline => {}
Tok::Indent => {
// This is like:
// ```py
// ```python
// if True:
// pass
// ```

View File

@@ -209,7 +209,7 @@ impl<'a> Importer<'a> {
// We also add a no-op edit to force conflicts with any other fixes that might try to
// remove the import. Consider:
//
// ```py
// ```python
// import sys
//
// quit()

View File

@@ -8,7 +8,7 @@ use itertools::Itertools;
use log::error;
use rustc_hash::FxHashMap;
use ruff_diagnostics::{Applicability, Diagnostic};
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::imports::ImportMap;
use ruff_python_ast::PySourceType;
use ruff_python_codegen::Stylist;
@@ -268,24 +268,13 @@ pub fn check_path(
}
// Update fix applicability to account for overrides
if !settings.extend_safe_fixes.is_empty() || !settings.extend_unsafe_fixes.is_empty() {
if !settings.fix_safety.is_empty() {
for diagnostic in &mut diagnostics {
if let Some(fix) = diagnostic.fix.take() {
// Enforce demotions over promotions so if someone puts a rule in both we are conservative
if fix.applicability().is_safe()
&& settings
.extend_unsafe_fixes
.contains(diagnostic.kind.rule())
{
diagnostic.set_fix(fix.with_applicability(Applicability::Unsafe));
} else if fix.applicability().is_unsafe()
&& settings.extend_safe_fixes.contains(diagnostic.kind.rule())
{
diagnostic.set_fix(fix.with_applicability(Applicability::Safe));
} else {
// Retain the existing fix (will be dropped from `.take()` otherwise)
diagnostic.set_fix(fix);
}
let fixed_applicability = settings
.fix_safety
.resolve_applicability(diagnostic.kind.rule(), fix.applicability());
diagnostic.set_fix(fix.with_applicability(fixed_applicability));
}
}
}

View File

@@ -54,9 +54,9 @@ impl Display for Diff<'_> {
let message = match self.fix.applicability() {
// TODO(zanieb): Adjust this messaging once it's user-facing
Applicability::Safe => "Fix",
Applicability::Unsafe => "Suggested fix",
Applicability::Display => "Possible fix",
Applicability::Safe => "Safe fix",
Applicability::Unsafe => "Unsafe fix",
Applicability::DisplayOnly => "Display-only fix",
};
writeln!(f, " {}", message.blue())?;

View File

@@ -64,6 +64,9 @@ pub enum Linter {
/// [flake8-async](https://pypi.org/project/flake8-async/)
#[prefix = "ASYNC"]
Flake8Async,
/// [flake8-trio](https://pypi.org/project/flake8-trio/)
#[prefix = "TRIO"]
Flake8Trio,
/// [flake8-bandit](https://pypi.org/project/flake8-bandit/)
#[prefix = "S"]
Flake8Bandit,

View File

@@ -163,11 +163,15 @@ mod tests {
"# ( user_content_type , _ )= TimelineEvent.objects.using(db_alias).get_or_create(",
&[]
));
assert!(comment_contains_code(
assert!(comment_contains_code("# )", &[]));
// This used to return true, but our parser has gotten a bit better
// at rejecting invalid Python syntax. And indeed, this is not valid
// Python code.
assert!(!comment_contains_code(
"# app_label=\"core\", model=\"user\"",
&[]
));
assert!(comment_contains_code("# )", &[]));
// TODO(charlie): This should be `true` under aggressive mode.
assert!(!comment_contains_code("#def foo():", &[]));

View File

@@ -66,7 +66,7 @@ pub(crate) fn commented_out_code(
if is_standalone_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {
let mut diagnostic = Diagnostic::new(CommentedOutCode, *range);
diagnostic.set_fix(Fix::display_edit(Edit::range_deletion(
diagnostic.set_fix(Fix::display_only_edit(Edit::range_deletion(
locator.full_lines_range(*range),
)));
diagnostics.push(diagnostic);

View File

@@ -10,7 +10,7 @@ ERA001.py:1:1: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
1 |-#import os
2 1 | # from foo import junk
3 2 | #a = 3
@@ -26,7 +26,7 @@ ERA001.py:2:1: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
1 1 | #import os
2 |-# from foo import junk
3 2 | #a = 3
@@ -44,7 +44,7 @@ ERA001.py:3:1: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
1 1 | #import os
2 2 | # from foo import junk
3 |-#a = 3
@@ -63,7 +63,7 @@ ERA001.py:5:1: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
2 2 | # from foo import junk
3 3 | #a = 3
4 4 | a = 4
@@ -82,7 +82,7 @@ ERA001.py:13:5: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
10 10 |
11 11 | # This is a real comment.
12 12 | # # This is a (nested) comment.
@@ -100,7 +100,7 @@ ERA001.py:21:5: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
18 18 |
19 19 | class A():
20 20 | pass
@@ -120,7 +120,7 @@ ERA001.py:26:5: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
23 23 |
24 24 | dictionary = {
25 25 | # "key1": 123, # noqa: ERA001
@@ -139,7 +139,7 @@ ERA001.py:27:5: ERA001 Found commented-out code
|
= help: Remove commented-out code
Possible fix
Display-only fix
24 24 | dictionary = {
25 25 | # "key1": 123, # noqa: ERA001
26 26 | # "key2": 456,

View File

@@ -252,7 +252,7 @@ annotation_presence.py:159:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
156 156 |
157 157 | class Foo:
158 158 | @decorator()
@@ -272,7 +272,7 @@ annotation_presence.py:165:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
162 162 |
163 163 | # Regression test for: https://github.com/astral-sh/ruff/issues/7711
164 164 | class Class:

View File

@@ -11,7 +11,7 @@ mypy_init_return.py:5:9: ANN204 [*] Missing return type annotation for special m
|
= help: Add `None` return type
Suggested fix
Unsafe fix
2 2 |
3 3 | # Error
4 4 | class Foo:
@@ -31,7 +31,7 @@ mypy_init_return.py:11:9: ANN204 [*] Missing return type annotation for special
|
= help: Add `None` return type
Suggested fix
Unsafe fix
8 8 |
9 9 | # Error
10 10 | class Foo:
@@ -59,7 +59,7 @@ mypy_init_return.py:47:9: ANN204 [*] Missing return type annotation for special
|
= help: Add `None` return type
Suggested fix
Unsafe fix
44 44 | # Error used to be ok for a moment since the mere presence
45 45 | # of a vararg falsely indicated that the function has a typed argument.
46 46 | class Foo:

View File

@@ -10,7 +10,7 @@ simple_magic_methods.py:2:9: ANN204 [*] Missing return type annotation for speci
|
= help: Add `None` return type
Suggested fix
Unsafe fix
1 1 | class Foo:
2 |- def __str__(self):
2 |+ def __str__(self) -> str:
@@ -28,7 +28,7 @@ simple_magic_methods.py:5:9: ANN204 [*] Missing return type annotation for speci
|
= help: Add `None` return type
Suggested fix
Unsafe fix
2 2 | def __str__(self):
3 3 | ...
4 4 |
@@ -48,7 +48,7 @@ simple_magic_methods.py:8:9: ANN204 [*] Missing return type annotation for speci
|
= help: Add `None` return type
Suggested fix
Unsafe fix
5 5 | def __repr__(self):
6 6 | ...
7 7 |
@@ -68,7 +68,7 @@ simple_magic_methods.py:11:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
8 8 | def __len__(self):
9 9 | ...
10 10 |
@@ -88,7 +88,7 @@ simple_magic_methods.py:14:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
11 11 | def __length_hint__(self):
12 12 | ...
13 13 |
@@ -108,7 +108,7 @@ simple_magic_methods.py:17:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
14 14 | def __init__(self):
15 15 | ...
16 16 |
@@ -128,7 +128,7 @@ simple_magic_methods.py:20:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
17 17 | def __del__(self):
18 18 | ...
19 19 |
@@ -148,7 +148,7 @@ simple_magic_methods.py:23:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
20 20 | def __bool__(self):
21 21 | ...
22 22 |
@@ -168,7 +168,7 @@ simple_magic_methods.py:26:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
23 23 | def __bytes__(self):
24 24 | ...
25 25 |
@@ -188,7 +188,7 @@ simple_magic_methods.py:29:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
26 26 | def __format__(self, format_spec):
27 27 | ...
28 28 |
@@ -208,7 +208,7 @@ simple_magic_methods.py:32:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
29 29 | def __contains__(self, item):
30 30 | ...
31 31 |
@@ -228,7 +228,7 @@ simple_magic_methods.py:35:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
32 32 | def __complex__(self):
33 33 | ...
34 34 |
@@ -248,7 +248,7 @@ simple_magic_methods.py:38:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
35 35 | def __int__(self):
36 36 | ...
37 37 |
@@ -268,7 +268,7 @@ simple_magic_methods.py:41:9: ANN204 [*] Missing return type annotation for spec
|
= help: Add `None` return type
Suggested fix
Unsafe fix
38 38 | def __float__(self):
39 39 | ...
40 40 |

View File

@@ -28,6 +28,7 @@ mod tests {
#[test_case(Rule::HardcodedTempFile, Path::new("S108.py"))]
#[test_case(Rule::HashlibInsecureHashFunction, Path::new("S324.py"))]
#[test_case(Rule::Jinja2AutoescapeFalse, Path::new("S701.py"))]
#[test_case(Rule::MakoTemplates, Path::new("S702.py"))]
#[test_case(Rule::LoggingConfigInsecureListen, Path::new("S612.py"))]
#[test_case(Rule::ParamikoCall, Path::new("S601.py"))]
#[test_case(Rule::RequestWithNoCertValidation, Path::new("S501.py"))]

View File

@@ -0,0 +1,57 @@
use crate::checkers::ast::Checker;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast};
use ruff_text_size::Ranged;
/// ## What it does
/// Checks for uses of the `mako` templates.
///
/// ## Why is this bad?
/// Mako templates allow HTML and JavaScript rendering by default, and are
/// inherently open to XSS attacks. Ensure variables in all templates are
/// properly sanitized via the `n`, `h` or `x` flags (depending on context).
/// For example, to HTML escape the variable `data`, use `${ data |h }`.
///
/// ## Example
/// ```python
/// from mako.template import Template
///
/// Template("hello")
/// ```
///
/// Use instead:
/// ```python
/// from mako.template import Template
///
/// Template("hello |h")
/// ```
///
/// ## References
/// - [Mako documentation](https://www.makotemplates.org/)
/// - [OpenStack security: Cross site scripting XSS](https://security.openstack.org/guidelines/dg_cross-site-scripting-xss.html)
/// - [Common Weakness Enumeration: CWE-80](https://cwe.mitre.org/data/definitions/80.html)
#[violation]
pub struct MakoTemplates;
impl Violation for MakoTemplates {
#[derive_message_formats]
fn message(&self) -> String {
format!(
"Mako templates allow HTML and JavaScript rendering by default and are inherently open to XSS attacks"
)
}
}
/// S702
pub(crate) fn mako_templates(checker: &mut Checker, call: &ast::ExprCall) {
if checker
.semantic()
.resolve_call_path(&call.func)
.is_some_and(|call_path| matches!(call_path.as_slice(), ["mako", "template", "Template"]))
{
checker
.diagnostics
.push(Diagnostic::new(MakoTemplates, call.func.range()));
}
}

View File

@@ -11,6 +11,7 @@ pub(crate) use hardcoded_tmp_directory::*;
pub(crate) use hashlib_insecure_hash_functions::*;
pub(crate) use jinja2_autoescape_false::*;
pub(crate) use logging_config_insecure_listen::*;
pub(crate) use mako_templates::*;
pub(crate) use paramiko_calls::*;
pub(crate) use request_with_no_cert_validation::*;
pub(crate) use request_without_timeout::*;
@@ -37,6 +38,7 @@ mod hardcoded_tmp_directory;
mod hashlib_insecure_hash_functions;
mod jinja2_autoescape_false;
mod logging_config_insecure_listen;
mod mako_templates;
mod paramiko_calls;
mod request_with_no_cert_validation;
mod request_without_timeout;

View File

@@ -0,0 +1,28 @@
---
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
---
S702.py:6:1: S702 Mako templates allow HTML and JavaScript rendering by default and are inherently open to XSS attacks
|
6 | Template("hello")
| ^^^^^^^^ S702
7 |
8 | mako.template.Template("hern")
|
S702.py:8:1: S702 Mako templates allow HTML and JavaScript rendering by default and are inherently open to XSS attacks
|
6 | Template("hello")
7 |
8 | mako.template.Template("hern")
| ^^^^^^^^^^^^^^^^^^^^^^ S702
9 | template.Template("hern")
|
S702.py:9:1: S702 Mako templates allow HTML and JavaScript rendering by default and are inherently open to XSS attacks
|
8 | mako.template.Template("hern")
9 | template.Template("hern")
| ^^^^^^^^^^^^^^^^^ S702
|

View File

@@ -27,6 +27,11 @@ use crate::checkers::ast::Checker;
/// raise AssertionError
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe, as changing an `assert` to a
/// `raise` will change the behavior of your program when running in
/// optimized mode (`python -O`).
///
/// ## References
/// - [Python documentation: `assert`](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement)
#[violation]

View File

@@ -50,6 +50,16 @@ use crate::checkers::ast::Checker;
/// return arg
/// ```
///
/// If the use of a singleton is intentional, assign the result call to a
/// module-level variable, and use that variable in the default argument:
/// ```python
/// ERROR = ValueError("Hosts weren't successfully added")
///
///
/// def add_host(error: Exception = ERROR) -> None:
/// ...
/// ```
///
/// ## Options
/// - `flake8-bugbear.extend-immutable-calls`
#[violation]
@@ -62,9 +72,9 @@ impl Violation for FunctionCallInDefaultArgument {
fn message(&self) -> String {
let FunctionCallInDefaultArgument { name } = self;
if let Some(name) = name {
format!("Do not perform function call `{name}` in argument defaults")
format!("Do not perform function call `{name}` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable")
} else {
format!("Do not perform function call in argument defaults")
format!("Do not perform function call in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable")
}
}
}

View File

@@ -12,7 +12,7 @@ B004.py:3:8: B004 [*] Using `hasattr(x, "__call__")` to test if x is callable is
|
= help: Replace with `callable()`
Fix
Safe fix
1 1 | def this_is_a_bug():
2 2 | o = object()
3 |- if hasattr(o, "__call__"):

View File

@@ -12,7 +12,7 @@ B006_1.py:3:22: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
1 1 | # Docstring followed by a newline
2 2 |
3 |-def foobar(foor, bar={}):

View File

@@ -12,7 +12,7 @@ B006_2.py:4:22: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
1 1 | # Docstring followed by whitespace with no newline
2 2 | # Regression test for https://github.com/astral-sh/ruff/issues/7155
3 3 |

View File

@@ -10,7 +10,7 @@ B006_3.py:4:22: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
1 1 | # Docstring with no newline
2 2 |
3 3 |

View File

@@ -10,7 +10,7 @@ B006_4.py:7:26: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
4 4 |
5 5 |
6 6 | class FormFeedIndent:

View File

@@ -9,7 +9,7 @@ B006_5.py:5:49: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
2 2 | # https://github.com/astral-sh/ruff/issues/7616
3 3 |
4 4 |
@@ -30,7 +30,7 @@ B006_5.py:9:61: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
6 6 | import os
7 7 |
8 8 |
@@ -53,7 +53,7 @@ B006_5.py:15:50: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
12 12 | return 2
13 13 |
14 14 |
@@ -76,7 +76,7 @@ B006_5.py:21:54: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
18 18 | import itertools
19 19 |
20 20 |
@@ -98,7 +98,7 @@ B006_5.py:25:55: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
22 22 | from os import path
23 23 |
24 24 |
@@ -121,7 +121,7 @@ B006_5.py:30:66: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
27 27 | from sys import version_info
28 28 |
29 29 |
@@ -144,7 +144,7 @@ B006_5.py:35:59: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
32 32 | from sys import version_info
33 33 |
34 34 |
@@ -167,7 +167,7 @@ B006_5.py:40:49: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
37 37 | import os
38 38 |
39 39 |
@@ -190,7 +190,7 @@ B006_5.py:45:49: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
42 42 | import os; import sys
43 43 |
44 44 |
@@ -212,7 +212,7 @@ B006_5.py:50:49: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
47 47 | import os; import sys; x = 1
48 48 |
49 49 |
@@ -234,7 +234,7 @@ B006_5.py:55:49: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
52 52 | import os; import sys
53 53 |
54 54 |
@@ -255,7 +255,7 @@ B006_5.py:59:49: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
56 56 | import os; import sys
57 57 |
58 58 |
@@ -275,7 +275,7 @@ B006_5.py:63:49: B006 [*] Do not use mutable data structures for argument defaul
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
60 60 | import os; import sys; x = 1
61 61 |
62 62 |

View File

@@ -11,7 +11,7 @@ B006_6.py:4:22: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
1 1 | # Import followed by whitespace with no newline
2 2 | # Same as B006_2.py, but import instead of docstring
3 3 |

View File

@@ -11,7 +11,7 @@ B006_7.py:4:22: B006 [*] Do not use mutable data structures for argument default
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
1 1 | # Import with no newline
2 2 | # Same as B006_3.py, but import instead of docstring
3 3 |

View File

@@ -9,7 +9,7 @@ B006_B008.py:63:25: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
60 60 | # Flag mutable literals/comprehensions
61 61 |
62 62 |
@@ -29,7 +29,7 @@ B006_B008.py:67:30: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
64 64 | ...
65 65 |
66 66 |
@@ -51,7 +51,7 @@ B006_B008.py:73:52: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
70 70 |
71 71 | class Foo:
72 72 | @staticmethod
@@ -74,7 +74,7 @@ B006_B008.py:77:31: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
74 74 | pass
75 75 |
76 76 |
@@ -105,7 +105,7 @@ B006_B008.py:85:20: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
82 82 | def single_line_func_wrong(value = {}): ...
83 83 |
84 84 |
@@ -125,7 +125,7 @@ B006_B008.py:89:20: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
86 86 | ...
87 87 |
88 88 |
@@ -145,7 +145,7 @@ B006_B008.py:93:32: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
90 90 | ...
91 91 |
92 92 |
@@ -165,7 +165,7 @@ B006_B008.py:97:26: B006 [*] Do not use mutable data structures for argument def
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
94 94 | ...
95 95 |
96 96 |
@@ -186,7 +186,7 @@ B006_B008.py:102:46: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
99 99 |
100 100 |
101 101 | # N.B. we're also flagging the function call in the comprehension
@@ -206,7 +206,7 @@ B006_B008.py:106:46: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
103 103 | pass
104 104 |
105 105 |
@@ -226,7 +226,7 @@ B006_B008.py:110:45: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
107 107 | pass
108 108 |
109 109 |
@@ -246,7 +246,7 @@ B006_B008.py:114:33: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
111 111 | pass
112 112 |
113 113 |
@@ -268,7 +268,7 @@ B006_B008.py:239:20: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
236 236 |
237 237 | # B006 and B008
238 238 | # We should handle arbitrary nesting of these B008.
@@ -290,7 +290,7 @@ B006_B008.py:276:27: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
273 273 |
274 274 |
275 275 | def mutable_annotations(
@@ -317,7 +317,7 @@ B006_B008.py:277:35: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
274 274 |
275 275 | def mutable_annotations(
276 276 | a: list[int] | None = [],
@@ -343,7 +343,7 @@ B006_B008.py:278:62: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
275 275 | def mutable_annotations(
276 276 | a: list[int] | None = [],
277 277 | b: Optional[Dict[int, int]] = {},
@@ -368,7 +368,7 @@ B006_B008.py:279:80: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
276 276 | a: list[int] | None = [],
277 277 | b: Optional[Dict[int, int]] = {},
278 278 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(),
@@ -389,7 +389,7 @@ B006_B008.py:284:52: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
281 281 | pass
282 282 |
283 283 |
@@ -411,7 +411,7 @@ B006_B008.py:288:52: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
285 285 | """Docstring"""
286 286 |
287 287 |
@@ -432,7 +432,7 @@ B006_B008.py:293:52: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
290 290 | ...
291 291 |
292 292 |
@@ -453,7 +453,7 @@ B006_B008.py:297:52: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
294 294 | """Docstring"""; ...
295 295 |
296 296 |
@@ -476,7 +476,7 @@ B006_B008.py:302:52: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
299 299 | ...
300 300 |
301 301 |
@@ -508,7 +508,7 @@ B006_B008.py:313:52: B006 [*] Do not use mutable data structures for argument de
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
310 310 | """Docstring"""
311 311 |
312 312 |

View File

@@ -21,7 +21,7 @@ B007.py:18:13: B007 [*] Loop control variable `k` not used within loop body
|
= help: Rename unused `k` to `_k`
Suggested fix
Unsafe fix
15 15 |
16 16 | for i in range(10):
17 17 | for j in range(10):
@@ -47,7 +47,7 @@ B007.py:30:13: B007 [*] Loop control variable `k` not used within loop body
|
= help: Rename unused `k` to `_k`
Suggested fix
Unsafe fix
27 27 | yield i, (j, (k, l))
28 28 |
29 29 |
@@ -110,7 +110,7 @@ B007.py:52:14: B007 [*] Loop control variable `bar` not used within loop body
|
= help: Rename unused `bar` to `_bar`
Suggested fix
Unsafe fix
49 49 |
50 50 | def f():
51 51 | # Fixable.
@@ -142,7 +142,7 @@ B007.py:68:14: B007 [*] Loop control variable `bar` not used within loop body
|
= help: Rename unused `bar` to `_bar`
Suggested fix
Unsafe fix
65 65 |
66 66 | def f():
67 67 | # Fixable.

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
---
B006_B008.py:102:61: B008 Do not perform function call `range` in argument defaults
B006_B008.py:102:61: B008 Do not perform function call `range` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
101 | # N.B. we're also flagging the function call in the comprehension
102 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]):
@@ -9,21 +9,21 @@ B006_B008.py:102:61: B008 Do not perform function call `range` in argument defau
103 | pass
|
B006_B008.py:106:64: B008 Do not perform function call `range` in argument defaults
B006_B008.py:106:64: B008 Do not perform function call `range` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
106 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}):
| ^^^^^^^^ B008
107 | pass
|
B006_B008.py:110:60: B008 Do not perform function call `range` in argument defaults
B006_B008.py:110:60: B008 Do not perform function call `range` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
110 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}):
| ^^^^^^^^ B008
111 | pass
|
B006_B008.py:126:39: B008 Do not perform function call `time.time` in argument defaults
B006_B008.py:126:39: B008 Do not perform function call `time.time` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
124 | # B008
125 | # Flag function calls as default args (including if they are part of a sub-expression)
@@ -32,21 +32,21 @@ B006_B008.py:126:39: B008 Do not perform function call `time.time` in argument d
127 | ...
|
B006_B008.py:130:12: B008 Do not perform function call `dt.datetime.now` in argument defaults
B006_B008.py:130:12: B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
130 | def f(when=dt.datetime.now() + dt.timedelta(days=7)):
| ^^^^^^^^^^^^^^^^^ B008
131 | pass
|
B006_B008.py:134:30: B008 Do not perform function call in argument defaults
B006_B008.py:134:30: B008 Do not perform function call in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
134 | def can_even_catch_lambdas(a=(lambda x: x)()):
| ^^^^^^^^^^^^^^^ B008
135 | ...
|
B006_B008.py:239:31: B008 Do not perform function call `dt.datetime.now` in argument defaults
B006_B008.py:239:31: B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
237 | # B006 and B008
238 | # We should handle arbitrary nesting of these B008.
@@ -55,7 +55,7 @@ B006_B008.py:239:31: B008 Do not perform function call `dt.datetime.now` in argu
240 | pass
|
B006_B008.py:245:22: B008 Do not perform function call `map` in argument defaults
B006_B008.py:245:22: B008 Do not perform function call `map` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
243 | # Don't flag nested B006 since we can't guarantee that
244 | # it isn't made mutable by the outer operation.
@@ -64,7 +64,7 @@ B006_B008.py:245:22: B008 Do not perform function call `map` in argument default
246 | pass
|
B006_B008.py:250:19: B008 Do not perform function call `random.randint` in argument defaults
B006_B008.py:250:19: B008 Do not perform function call `random.randint` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
249 | # B008-ception.
250 | def nested_b008(a=random.randint(0, dt.datetime.now().year)):
@@ -72,7 +72,7 @@ B006_B008.py:250:19: B008 Do not perform function call `random.randint` in argum
251 | pass
|
B006_B008.py:250:37: B008 Do not perform function call `dt.datetime.now` in argument defaults
B006_B008.py:250:37: B008 Do not perform function call `dt.datetime.now` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
249 | # B008-ception.
250 | def nested_b008(a=random.randint(0, dt.datetime.now().year)):

View File

@@ -11,7 +11,7 @@ B009_B010.py:19:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
16 16 | getattr(foo, "__123abc")
17 17 |
18 18 | # Invalid usage
@@ -32,7 +32,7 @@ B009_B010.py:20:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
17 17 |
18 18 | # Invalid usage
19 19 | getattr(foo, "bar")
@@ -53,7 +53,7 @@ B009_B010.py:21:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
18 18 | # Invalid usage
19 19 | getattr(foo, "bar")
20 20 | getattr(foo, "_123abc")
@@ -74,7 +74,7 @@ B009_B010.py:22:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
19 19 | getattr(foo, "bar")
20 20 | getattr(foo, "_123abc")
21 21 | getattr(foo, "__123abc__")
@@ -95,7 +95,7 @@ B009_B010.py:23:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
20 20 | getattr(foo, "_123abc")
21 21 | getattr(foo, "__123abc__")
22 22 | getattr(foo, "abc123")
@@ -116,7 +116,7 @@ B009_B010.py:24:15: B009 [*] Do not call `getattr` with a constant attribute val
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
21 21 | getattr(foo, "__123abc__")
22 22 | getattr(foo, "abc123")
23 23 | getattr(foo, r"abc123")
@@ -137,7 +137,7 @@ B009_B010.py:25:4: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
22 22 | getattr(foo, "abc123")
23 23 | getattr(foo, r"abc123")
24 24 | _ = lambda x: getattr(x, "bar")
@@ -158,7 +158,7 @@ B009_B010.py:27:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
24 24 | _ = lambda x: getattr(x, "bar")
25 25 | if getattr(x, "bar"):
26 26 | pass
@@ -179,7 +179,7 @@ B009_B010.py:28:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
25 25 | if getattr(x, "bar"):
26 26 | pass
27 27 | getattr(1, "real")
@@ -200,7 +200,7 @@ B009_B010.py:29:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
26 26 | pass
27 27 | getattr(1, "real")
28 28 | getattr(1., "real")
@@ -221,7 +221,7 @@ B009_B010.py:30:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
27 27 | getattr(1, "real")
28 28 | getattr(1., "real")
29 29 | getattr(1.0, "real")
@@ -242,7 +242,7 @@ B009_B010.py:31:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
28 28 | getattr(1., "real")
29 29 | getattr(1.0, "real")
30 30 | getattr(1j, "real")
@@ -263,7 +263,7 @@ B009_B010.py:32:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
29 29 | getattr(1.0, "real")
30 30 | getattr(1j, "real")
31 31 | getattr(True, "real")
@@ -284,7 +284,7 @@ B009_B010.py:33:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
30 30 | getattr(1j, "real")
31 31 | getattr(True, "real")
32 32 | getattr(x := 1, "real")
@@ -304,7 +304,7 @@ B009_B010.py:34:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
31 31 | getattr(True, "real")
32 32 | getattr(x := 1, "real")
33 33 | getattr(x + y, "real")
@@ -326,7 +326,7 @@ B009_B010.py:58:8: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
55 55 | setattr(foo.bar, r"baz", None)
56 56 |
57 57 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1722458885
@@ -345,7 +345,7 @@ B009_B010.py:65:1: B009 [*] Do not call `getattr` with a constant attribute valu
|
= help: Replace `getattr` with attribute access
Fix
Safe fix
62 62 | setattr(*foo, "bar", None)
63 63 |
64 64 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1739800901

View File

@@ -11,7 +11,7 @@ B009_B010.py:50:1: B010 [*] Do not call `setattr` with a constant attribute valu
|
= help: Replace `setattr` with assignment
Fix
Safe fix
47 47 | pass
48 48 |
49 49 | # Invalid usage
@@ -32,7 +32,7 @@ B009_B010.py:51:1: B010 [*] Do not call `setattr` with a constant attribute valu
|
= help: Replace `setattr` with assignment
Fix
Safe fix
48 48 |
49 49 | # Invalid usage
50 50 | setattr(foo, "bar", None)
@@ -53,7 +53,7 @@ B009_B010.py:52:1: B010 [*] Do not call `setattr` with a constant attribute valu
|
= help: Replace `setattr` with assignment
Fix
Safe fix
49 49 | # Invalid usage
50 50 | setattr(foo, "bar", None)
51 51 | setattr(foo, "_123abc", None)
@@ -74,7 +74,7 @@ B009_B010.py:53:1: B010 [*] Do not call `setattr` with a constant attribute valu
|
= help: Replace `setattr` with assignment
Fix
Safe fix
50 50 | setattr(foo, "bar", None)
51 51 | setattr(foo, "_123abc", None)
52 52 | setattr(foo, "__123abc__", None)
@@ -94,7 +94,7 @@ B009_B010.py:54:1: B010 [*] Do not call `setattr` with a constant attribute valu
|
= help: Replace `setattr` with assignment
Fix
Safe fix
51 51 | setattr(foo, "_123abc", None)
52 52 | setattr(foo, "__123abc__", None)
53 53 | setattr(foo, "abc123", None)
@@ -115,7 +115,7 @@ B009_B010.py:55:1: B010 [*] Do not call `setattr` with a constant attribute valu
|
= help: Replace `setattr` with assignment
Fix
Safe fix
52 52 | setattr(foo, "__123abc__", None)
53 53 | setattr(foo, "abc123", None)
54 54 | setattr(foo, r"abc123", None)

View File

@@ -11,7 +11,7 @@ B011.py:8:8: B011 [*] Do not `assert False` (`python -O` removes these calls), r
|
= help: Replace `assert False`
Suggested fix
Unsafe fix
5 5 | """
6 6 |
7 7 | assert 1 != 2
@@ -29,7 +29,7 @@ B011.py:10:8: B011 [*] Do not `assert False` (`python -O` removes these calls),
|
= help: Replace `assert False`
Suggested fix
Unsafe fix
7 7 | assert 1 != 2
8 8 | assert False
9 9 | assert 1 != 2, "message"

View File

@@ -12,7 +12,7 @@ B013.py:5:8: B013 [*] A length-one tuple literal is redundant in exception handl
|
= help: Replace with `except ValueError`
Fix
Safe fix
2 2 |
3 3 | try:
4 4 | pass
@@ -33,7 +33,7 @@ B013.py:11:8: B013 [*] A length-one tuple literal is redundant in exception hand
|
= help: Replace with `except retriable_exceptions`
Fix
Safe fix
8 8 | pass
9 9 | except (ImportError, TypeError):
10 10 | pass
@@ -53,7 +53,7 @@ B013.py:13:7: B013 [*] A length-one tuple literal is redundant in exception hand
|
= help: Replace with `except ValueError`
Fix
Safe fix
10 10 | pass
11 11 | except (*retriable_exceptions,):
12 12 | pass

View File

@@ -12,7 +12,7 @@ B014.py:17:8: B014 [*] Exception handler with duplicate exception: `OSError`
|
= help: De-duplicate exceptions
Fix
Safe fix
14 14 |
15 15 | try:
16 16 | pass
@@ -33,7 +33,7 @@ B014.py:28:8: B014 [*] Exception handler with duplicate exception: `MyError`
|
= help: De-duplicate exceptions
Fix
Safe fix
25 25 |
26 26 | try:
27 27 | pass
@@ -54,7 +54,7 @@ B014.py:49:8: B014 [*] Exception handler with duplicate exception: `re.error`
|
= help: De-duplicate exceptions
Fix
Safe fix
46 46 |
47 47 | try:
48 48 | pass
@@ -74,7 +74,7 @@ B014.py:82:8: B014 [*] Exception handler with duplicate exception: `ValueError`
|
= help: De-duplicate exceptions
Fix
Safe fix
79 79 | # Regression test for: https://github.com/astral-sh/ruff/issues/6412
80 80 | try:
81 81 | pass
@@ -94,7 +94,7 @@ B014.py:89:7: B014 [*] Exception handler with duplicate exception: `re.error`
|
= help: De-duplicate exceptions
Fix
Safe fix
86 86 | # Regression test for: https://github.com/astral-sh/ruff/issues/7455#issuecomment-1739801758
87 87 | try:
88 88 | pas

View File

@@ -9,7 +9,7 @@ B006_extended.py:17:55: B006 [*] Do not use mutable data structures for argument
|
= help: Replace with `None`; initialize within function
Suggested fix
Unsafe fix
14 14 | ...
15 15 |
16 16 |

View File

@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
---
B008_extended.py:24:51: B008 Do not perform function call `Depends` in argument defaults
B008_extended.py:24:51: B008 Do not perform function call `Depends` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
|
24 | def error_due_to_missing_import(data: List[str] = Depends(None)):
| ^^^^^^^^^^^^^ B008

View File

@@ -12,7 +12,7 @@ COM81.py:4:18: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
1 1 | # ==> bad_function_call.py <==
2 2 | bad_function_call(
3 3 | param1='test',
@@ -32,7 +32,7 @@ COM81.py:10:6: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
7 7 | bad_list = [
8 8 | 1,
9 9 | 2,
@@ -53,7 +53,7 @@ COM81.py:16:6: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
13 13 | bad_list_with_comment = [
14 14 | 1,
15 15 | 2,
@@ -72,7 +72,7 @@ COM81.py:23:6: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
20 20 | bad_list_with_extra_empty = [
21 21 | 1,
22 22 | 2,
@@ -159,7 +159,7 @@ COM81.py:70:8: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
67 67 | pass
68 68 |
69 69 | {'foo': foo}['foo'](
@@ -178,7 +178,7 @@ COM81.py:78:8: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
75 75 | )
76 76 |
77 77 | (foo)(
@@ -197,7 +197,7 @@ COM81.py:86:8: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
83 83 | )
84 84 |
85 85 | [foo][0](
@@ -217,7 +217,7 @@ COM81.py:152:6: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
149 149 |
150 150 | # ==> keyword_before_parenth_form/base_bad.py <==
151 151 | from x import (
@@ -237,7 +237,7 @@ COM81.py:158:11: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
155 155 | assert(
156 156 | SyntaxWarning,
157 157 | ThrownHere,
@@ -258,7 +258,7 @@ COM81.py:293:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
290 290 |
291 291 | # ==> multiline_bad_dict.py <==
292 292 | multiline_bad_dict = {
@@ -279,7 +279,7 @@ COM81.py:304:14: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
301 301 |
302 302 | def func_bad(
303 303 | a = 3,
@@ -300,7 +300,7 @@ COM81.py:310:14: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
307 307 |
308 308 | # ==> multiline_bad_function_one_param.py <==
309 309 | def func(
@@ -319,7 +319,7 @@ COM81.py:316:10: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
313 313 |
314 314 |
315 315 | func(
@@ -339,7 +339,7 @@ COM81.py:322:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
319 319 | # ==> multiline_bad_or_dict.py <==
320 320 | multiline_bad_or_dict = {
321 321 | "good": True or False,
@@ -359,7 +359,7 @@ COM81.py:368:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
365 365 |
366 366 | multiline_index_access[
367 367 | "probably fine",
@@ -379,7 +379,7 @@ COM81.py:375:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
372 372 | "fine",
373 373 | "fine",
374 374 | :
@@ -399,7 +399,7 @@ COM81.py:404:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
401 401 | "fine",
402 402 | "fine"
403 403 | :
@@ -419,7 +419,7 @@ COM81.py:432:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
429 429 | "fine"
430 430 | :
431 431 | "fine",
@@ -439,7 +439,7 @@ COM81.py:485:21: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
482 482 | )
483 483 |
484 484 | # ==> prohibited.py <==
@@ -460,7 +460,7 @@ COM81.py:487:13: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
484 484 | # ==> prohibited.py <==
485 485 | foo = ['a', 'b', 'c',]
486 486 |
@@ -480,7 +480,7 @@ COM81.py:489:18: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
486 486 |
487 487 | bar = { a: b,}
488 488 |
@@ -501,7 +501,7 @@ COM81.py:494:6: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
491 491 |
492 492 | (0,)
493 493 |
@@ -522,7 +522,7 @@ COM81.py:496:21: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
493 493 |
494 494 | (0, 1,)
495 495 |
@@ -543,7 +543,7 @@ COM81.py:498:13: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
495 495 |
496 496 | foo = ['a', 'b', 'c', ]
497 497 |
@@ -563,7 +563,7 @@ COM81.py:500:18: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
497 497 |
498 498 | bar = { a: b, }
499 499 |
@@ -584,7 +584,7 @@ COM81.py:505:6: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
502 502 |
503 503 | (0, )
504 504 |
@@ -605,7 +605,7 @@ COM81.py:511:10: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
508 508 |
509 509 | image[:,]
510 510 |
@@ -626,7 +626,7 @@ COM81.py:513:9: COM819 [*] Trailing comma prohibited
|
= help: Remove trailing comma
Fix
Safe fix
510 510 |
511 511 | image[:,:,]
512 512 |
@@ -647,7 +647,7 @@ COM81.py:519:13: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
516 516 | def function(
517 517 | foo,
518 518 | bar,
@@ -668,7 +668,7 @@ COM81.py:526:10: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
523 523 | def function(
524 524 | foo,
525 525 | bar,
@@ -689,7 +689,7 @@ COM81.py:534:16: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
531 531 | foo,
532 532 | bar,
533 533 | *args,
@@ -709,7 +709,7 @@ COM81.py:541:13: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
538 538 | result = function(
539 539 | foo,
540 540 | bar,
@@ -729,7 +729,7 @@ COM81.py:547:24: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
544 544 | result = function(
545 545 | foo,
546 546 | bar,
@@ -750,7 +750,7 @@ COM81.py:554:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
551 551 | ham,
552 552 | spam,
553 553 | *args,
@@ -769,7 +769,7 @@ COM81.py:561:13: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
558 558 | # In python 3.5 if it's not a function def, commas are mandatory.
559 559 |
560 560 | foo(
@@ -788,7 +788,7 @@ COM81.py:565:13: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
562 562 | )
563 563 |
564 564 | {
@@ -807,7 +807,7 @@ COM81.py:573:10: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
570 570 | )
571 571 |
572 572 | {
@@ -826,7 +826,7 @@ COM81.py:577:10: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
574 574 | }
575 575 |
576 576 | [
@@ -847,7 +847,7 @@ COM81.py:583:10: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
580 580 | def foo(
581 581 | ham,
582 582 | spam,
@@ -868,7 +868,7 @@ COM81.py:590:13: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
587 587 | def foo(
588 588 | ham,
589 589 | spam,
@@ -889,7 +889,7 @@ COM81.py:598:15: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
595 595 | ham,
596 596 | spam,
597 597 | *args,
@@ -909,7 +909,7 @@ COM81.py:627:20: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
624 624 | result = function(
625 625 | foo,
626 626 | bar,
@@ -929,7 +929,7 @@ COM81.py:632:42: COM812 [*] Trailing comma missing
|
= help: Add trailing comma
Fix
Safe fix
629 629 |
630 630 | # Make sure the COM812 and UP034 rules don't fix simultaneously and cause a syntax error.
631 631 | the_first_one = next(

View File

@@ -1,4 +1,4 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::Ranged;
@@ -83,13 +83,14 @@ pub(crate) fn unnecessary_call_around_sorted(
expr.range(),
);
diagnostic.try_set_fix(|| {
let edit =
fixes::fix_unnecessary_call_around_sorted(expr, checker.locator(), checker.stylist())?;
if outer.id == "reversed" {
Ok(Fix::unsafe_edit(edit))
} else {
Ok(Fix::safe_edit(edit))
}
Ok(Fix::applicable_edit(
fixes::fix_unnecessary_call_around_sorted(expr, checker.locator(), checker.stylist())?,
if outer.id == "reversed" {
Applicability::Unsafe
} else {
Applicability::Safe
},
))
});
checker.diagnostics.push(diagnostic);
}

View File

@@ -10,7 +10,7 @@ C400.py:1:5: C400 [*] Unnecessary generator (rewrite as a `list` comprehension)
|
= help: Rewrite as a `list` comprehension
Suggested fix
Unsafe fix
1 |-x = list(x for x in range(3))
1 |+x = [x for x in range(3)]
2 2 | x = list(
@@ -28,7 +28,7 @@ C400.py:2:5: C400 [*] Unnecessary generator (rewrite as a `list` comprehension)
|
= help: Rewrite as a `list` comprehension
Suggested fix
Unsafe fix
1 1 | x = list(x for x in range(3))
2 |-x = list(
2 |+x = [

View File

@@ -10,7 +10,7 @@ C401.py:1:5: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
1 |-x = set(x for x in range(3))
1 |+x = {x for x in range(3)}
2 2 | x = set(x for x in range(3))
@@ -27,7 +27,7 @@ C401.py:2:5: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
1 1 | x = set(x for x in range(3))
2 |-x = set(x for x in range(3))
2 |+x = {x for x in range(3)}
@@ -46,7 +46,7 @@ C401.py:3:8: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
1 1 | x = set(x for x in range(3))
2 2 | x = set(x for x in range(3))
3 |-y = f"{set(a if a < 6 else 0 for a in range(3))}"
@@ -65,7 +65,7 @@ C401.py:4:17: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
1 1 | x = set(x for x in range(3))
2 2 | x = set(x for x in range(3))
3 3 | y = f"{set(a if a < 6 else 0 for a in range(3))}"
@@ -84,7 +84,7 @@ C401.py:5:16: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
2 2 | x = set(x for x in range(3))
3 3 | y = f"{set(a if a < 6 else 0 for a in range(3))}"
4 4 | _ = "{}".format(set(a if a < 6 else 0 for a in range(3)))
@@ -103,7 +103,7 @@ C401.py:12:16: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
9 9 | return x
10 10 |
11 11 |
@@ -123,7 +123,7 @@ C401.py:13:16: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
10 10 |
11 11 |
12 12 | print(f'Hello {set(a for a in "abc")} World')
@@ -144,7 +144,7 @@ C401.py:14:16: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
11 11 |
12 12 | print(f'Hello {set(a for a in "abc")} World')
13 13 | print(f"Hello {set(a for a in 'abc')} World")
@@ -164,7 +164,7 @@ C401.py:15:10: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
12 12 | print(f'Hello {set(a for a in "abc")} World')
13 13 | print(f"Hello {set(a for a in 'abc')} World")
14 14 | print(f"Hello {set(f(a) for a in 'abc')} World")
@@ -184,7 +184,7 @@ C401.py:15:34: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
12 12 | print(f'Hello {set(a for a in "abc")} World')
13 13 | print(f"Hello {set(a for a in 'abc')} World")
14 14 | print(f"Hello {set(f(a) for a in 'abc')} World")
@@ -205,7 +205,7 @@ C401.py:16:11: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
13 13 | print(f"Hello {set(a for a in 'abc')} World")
14 14 | print(f"Hello {set(f(a) for a in 'abc')} World")
15 15 | print(f"{set(a for a in 'abc') - set(a for a in 'ab')}")
@@ -226,7 +226,7 @@ C401.py:16:35: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
13 13 | print(f"Hello {set(a for a in 'abc')} World")
14 14 | print(f"Hello {set(f(a) for a in 'abc')} World")
15 15 | print(f"{set(a for a in 'abc') - set(a for a in 'ab')}")
@@ -245,7 +245,7 @@ C401.py:20:12: C401 [*] Unnecessary generator (rewrite as a `set` comprehension)
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
17 17 |
18 18 | # The fix generated for this diagnostic is incorrect, as we add additional space
19 19 | # around the set comprehension.

View File

@@ -10,7 +10,7 @@ C402.py:1:1: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
1 |-dict((x, x) for x in range(3))
1 |+{x: x for x in range(3)}
2 2 | dict(
@@ -29,7 +29,7 @@ C402.py:2:1: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
1 1 | dict((x, x) for x in range(3))
2 |-dict(
3 |- (x, x) for x in range(3)
@@ -52,7 +52,7 @@ C402.py:6:8: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
3 3 | (x, x) for x in range(3)
4 4 | )
5 5 | dict(((x, x) for x in range(3)), z=3)
@@ -73,7 +73,7 @@ C402.py:7:16: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
4 4 | )
5 5 | dict(((x, x) for x in range(3)), z=3)
6 6 | y = f'{dict((x, x) for x in range(3))}'
@@ -94,7 +94,7 @@ C402.py:8:16: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
5 5 | dict(((x, x) for x in range(3)), z=3)
6 6 | y = f'{dict((x, x) for x in range(3))}'
7 7 | print(f'Hello {dict((x, x) for x in range(3))} World')
@@ -114,7 +114,7 @@ C402.py:9:16: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
6 6 | y = f'{dict((x, x) for x in range(3))}'
7 7 | print(f'Hello {dict((x, x) for x in range(3))} World')
8 8 | print(f"Hello {dict((x, x) for x in 'abc')} World")
@@ -135,7 +135,7 @@ C402.py:10:16: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
7 7 | print(f'Hello {dict((x, x) for x in range(3))} World')
8 8 | print(f"Hello {dict((x, x) for x in 'abc')} World")
9 9 | print(f'Hello {dict((x, x) for x in "abc")} World')
@@ -155,7 +155,7 @@ C402.py:12:4: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
9 9 | print(f'Hello {dict((x, x) for x in "abc")} World')
10 10 | print(f'Hello {dict((x,x) for x in "abc")} World')
11 11 |
@@ -175,7 +175,7 @@ C402.py:12:37: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
9 9 | print(f'Hello {dict((x, x) for x in "abc")} World')
10 10 | print(f'Hello {dict((x,x) for x in "abc")} World')
11 11 |
@@ -195,7 +195,7 @@ C402.py:13:5: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
10 10 | print(f'Hello {dict((x,x) for x in "abc")} World')
11 11 |
12 12 | f'{dict((x, x) for x in range(3)) | dict((x, x) for x in range(3))}'
@@ -215,7 +215,7 @@ C402.py:13:38: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
10 10 | print(f'Hello {dict((x,x) for x in "abc")} World')
11 11 |
12 12 | f'{dict((x, x) for x in range(3)) | dict((x, x) for x in range(3))}'
@@ -236,7 +236,7 @@ C402.py:18:16: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
15 15 | def f(x):
16 16 | return x
17 17 |
@@ -256,7 +256,7 @@ C402.py:21:1: C402 [*] Unnecessary generator (rewrite as a `dict` comprehension)
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
18 18 | print(f'Hello {dict((x,f(x)) for x in "abc")} World')
19 19 |
20 20 | # Regression test for: https://github.com/astral-sh/ruff/issues/7086

View File

@@ -10,7 +10,7 @@ C403.py:1:5: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` compr
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
1 |-s = set([x for x in range(3)])
1 |+s = {x for x in range(3)}
2 2 | s = set(
@@ -30,7 +30,7 @@ C403.py:2:5: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` compr
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
1 1 | s = set([x for x in range(3)])
2 |-s = set(
3 |- [x for x in range(3)]
@@ -52,7 +52,7 @@ C403.py:6:8: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` compr
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
3 3 | [x for x in range(3)]
4 4 | )
5 5 |
@@ -72,7 +72,7 @@ C403.py:7:8: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` compr
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
4 4 | )
5 5 |
6 6 | s = f"{set([x for x in 'ab'])}"
@@ -93,7 +93,7 @@ C403.py:12:8: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` comp
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
9 9 | def f(x):
10 10 | return x
11 11 |
@@ -113,7 +113,7 @@ C403.py:14:9: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` comp
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
11 11 |
12 12 | s = f"{set([f(x) for x in 'ab'])}"
13 13 |
@@ -131,7 +131,7 @@ C403.py:14:34: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` com
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
11 11 |
12 12 | s = f"{set([f(x) for x in 'ab'])}"
13 13 |
@@ -147,7 +147,7 @@ C403.py:15:8: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` comp
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
12 12 | s = f"{set([f(x) for x in 'ab'])}"
13 13 |
14 14 | s = f"{ set([x for x in 'ab']) | set([x for x in 'ab']) }"
@@ -162,7 +162,7 @@ C403.py:15:33: C403 [*] Unnecessary `list` comprehension (rewrite as a `set` com
|
= help: Rewrite as a `set` comprehension
Suggested fix
Unsafe fix
12 12 | s = f"{set([f(x) for x in 'ab'])}"
13 13 |
14 14 | s = f"{ set([x for x in 'ab']) | set([x for x in 'ab']) }"

View File

@@ -9,7 +9,7 @@ C404.py:1:1: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` comp
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
1 |-dict([(i, i) for i in range(3)])
1 |+{i: i for i in range(3)}
2 2 | dict([(i, i) for i in range(3)], z=4)
@@ -27,7 +27,7 @@ C404.py:7:4: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` comp
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
4 4 | def f(x):
5 5 | return x
6 6 |
@@ -47,7 +47,7 @@ C404.py:8:4: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` comp
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
5 5 | return x
6 6 |
7 7 | f'{dict([(s,s) for s in "ab"])}'
@@ -67,7 +67,7 @@ C404.py:9:4: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` comp
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
6 6 |
7 7 | f'{dict([(s,s) for s in "ab"])}'
8 8 | f"{dict([(s,s) for s in 'ab'])}"
@@ -88,7 +88,7 @@ C404.py:10:4: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` com
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
7 7 | f'{dict([(s,s) for s in "ab"])}'
8 8 | f"{dict([(s,s) for s in 'ab'])}"
9 9 | f"{dict([(s, s) for s in 'ab'])}"
@@ -108,7 +108,7 @@ C404.py:12:4: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` com
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
9 9 | f"{dict([(s, s) for s in 'ab'])}"
10 10 | f"{dict([(s,f(s)) for s in 'ab'])}"
11 11 |
@@ -128,7 +128,7 @@ C404.py:12:34: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` co
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
9 9 | f"{dict([(s, s) for s in 'ab'])}"
10 10 | f"{dict([(s,f(s)) for s in 'ab'])}"
11 11 |
@@ -148,7 +148,7 @@ C404.py:13:5: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` com
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
10 10 | f"{dict([(s,f(s)) for s in 'ab'])}"
11 11 |
12 12 | f'{dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"])}'
@@ -168,7 +168,7 @@ C404.py:13:35: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` co
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
10 10 | f"{dict([(s,f(s)) for s in 'ab'])}"
11 11 |
12 12 | f'{dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"])}'
@@ -186,7 +186,7 @@ C404.py:16:14: C404 [*] Unnecessary `list` comprehension (rewrite as a `dict` co
|
= help: Rewrite as a `dict` comprehension
Suggested fix
Unsafe fix
13 13 | f'{ dict([(s,s) for s in "ab"]) | dict([(s,s) for s in "ab"]) }'
14 14 |
15 15 | # Regression test for: https://github.com/astral-sh/ruff/issues/7087

View File

@@ -10,7 +10,7 @@ C405.py:1:1: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
1 |-set([1, 2])
1 |+{1, 2}
2 2 | set((1, 2))
@@ -27,7 +27,7 @@ C405.py:2:1: C405 [*] Unnecessary `tuple` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
1 1 | set([1, 2])
2 |-set((1, 2))
2 |+{1, 2}
@@ -46,7 +46,7 @@ C405.py:3:1: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
1 1 | set([1, 2])
2 2 | set((1, 2))
3 |-set([])
@@ -66,7 +66,7 @@ C405.py:4:1: C405 [*] Unnecessary `tuple` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
1 1 | set([1, 2])
2 2 | set((1, 2))
3 3 | set([])
@@ -88,7 +88,7 @@ C405.py:6:1: C405 [*] Unnecessary `tuple` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
3 3 | set([])
4 4 | set(())
5 5 | set()
@@ -111,7 +111,7 @@ C405.py:7:1: C405 [*] Unnecessary `tuple` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
4 4 | set(())
5 5 | set()
6 6 | set((1,))
@@ -137,7 +137,7 @@ C405.py:10:1: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
7 7 | set((
8 8 | 1,
9 9 | ))
@@ -163,7 +163,7 @@ C405.py:13:1: C405 [*] Unnecessary `tuple` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
10 10 | set([
11 11 | 1,
12 12 | ])
@@ -188,7 +188,7 @@ C405.py:16:1: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
13 13 | set(
14 14 | (1,)
15 15 | )
@@ -211,7 +211,7 @@ C405.py:19:4: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
16 16 | set(
17 17 | [1,]
18 18 | )
@@ -231,7 +231,7 @@ C405.py:20:4: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
17 17 | [1,]
18 18 | )
19 19 | f"{set([1,2,3])}"
@@ -252,7 +252,7 @@ C405.py:21:4: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
18 18 | )
19 19 | f"{set([1,2,3])}"
20 20 | f"{set(['a', 'b'])}"
@@ -273,7 +273,7 @@ C405.py:23:4: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
20 20 | f"{set(['a', 'b'])}"
21 21 | f'{set(["a", "b"])}'
22 22 |
@@ -294,7 +294,7 @@ C405.py:23:22: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
20 20 | f"{set(['a', 'b'])}"
21 21 | f'{set(["a", "b"])}'
22 22 |
@@ -314,7 +314,7 @@ C405.py:24:5: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
21 21 | f'{set(["a", "b"])}'
22 22 |
23 23 | f"{set(['a', 'b']) - set(['a'])}"
@@ -333,7 +333,7 @@ C405.py:24:23: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
21 21 | f'{set(["a", "b"])}'
22 22 |
23 23 | f"{set(['a', 'b']) - set(['a'])}"
@@ -352,7 +352,7 @@ C405.py:25:6: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
22 22 |
23 23 | f"{set(['a', 'b']) - set(['a'])}"
24 24 | f"{ set(['a', 'b']) - set(['a']) }"
@@ -370,7 +370,7 @@ C405.py:25:24: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
22 22 |
23 23 | f"{set(['a', 'b']) - set(['a'])}"
24 24 | f"{ set(['a', 'b']) - set(['a']) }"
@@ -387,7 +387,7 @@ C405.py:26:7: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
23 23 | f"{set(['a', 'b']) - set(['a'])}"
24 24 | f"{ set(['a', 'b']) - set(['a']) }"
25 25 | f"a {set(['a', 'b']) - set(['a'])} b"
@@ -403,7 +403,7 @@ C405.py:26:25: C405 [*] Unnecessary `list` literal (rewrite as a `set` literal)
|
= help: Rewrite as a `set` literal
Suggested fix
Unsafe fix
23 23 | f"{set(['a', 'b']) - set(['a'])}"
24 24 | f"{ set(['a', 'b']) - set(['a']) }"
25 25 | f"a {set(['a', 'b']) - set(['a'])} b"

View File

@@ -10,7 +10,7 @@ C406.py:1:6: C406 [*] Unnecessary `list` literal (rewrite as a `dict` literal)
|
= help: Rewrite as a `dict` literal
Suggested fix
Unsafe fix
1 |-d1 = dict([(1, 2)])
1 |+d1 = {1: 2}
2 2 | d2 = dict(((1, 2),))
@@ -27,7 +27,7 @@ C406.py:2:6: C406 [*] Unnecessary `tuple` literal (rewrite as a `dict` literal)
|
= help: Rewrite as a `dict` literal
Suggested fix
Unsafe fix
1 1 | d1 = dict([(1, 2)])
2 |-d2 = dict(((1, 2),))
2 |+d2 = {1: 2,}
@@ -46,7 +46,7 @@ C406.py:3:6: C406 [*] Unnecessary `list` literal (rewrite as a `dict` literal)
|
= help: Rewrite as a `dict` literal
Suggested fix
Unsafe fix
1 1 | d1 = dict([(1, 2)])
2 2 | d2 = dict(((1, 2),))
3 |-d3 = dict([])
@@ -64,7 +64,7 @@ C406.py:4:6: C406 [*] Unnecessary `tuple` literal (rewrite as a `dict` literal)
|
= help: Rewrite as a `dict` literal
Suggested fix
Unsafe fix
1 1 | d1 = dict([(1, 2)])
2 2 | d2 = dict(((1, 2),))
3 3 | d3 = dict([])

View File

@@ -10,7 +10,7 @@ C408.py:1:5: C408 [*] Unnecessary `tuple` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 |-t = tuple()
1 |+t = ()
2 2 | l = list()
@@ -27,7 +27,7 @@ C408.py:2:5: C408 [*] Unnecessary `list` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 1 | t = tuple()
2 |-l = list()
2 |+l = []
@@ -46,7 +46,7 @@ C408.py:3:6: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 1 | t = tuple()
2 2 | l = list()
3 |-d1 = dict()
@@ -65,7 +65,7 @@ C408.py:4:6: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 1 | t = tuple()
2 2 | l = list()
3 3 | d1 = dict()
@@ -86,7 +86,7 @@ C408.py:14:4: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
11 11 |
12 12 | a = list()
13 13 |
@@ -106,7 +106,7 @@ C408.py:15:4: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
12 12 | a = list()
13 13 |
14 14 | f"{dict(x='y')}"
@@ -126,7 +126,7 @@ C408.py:16:4: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
13 13 |
14 14 | f"{dict(x='y')}"
15 15 | f'{dict(x="y")}'
@@ -147,7 +147,7 @@ C408.py:17:6: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
14 14 | f"{dict(x='y')}"
15 15 | f'{dict(x="y")}'
16 16 | f"{dict()}"
@@ -168,7 +168,7 @@ C408.py:19:4: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
16 16 | f"{dict()}"
17 17 | f"a {dict()} b"
18 18 |
@@ -189,7 +189,7 @@ C408.py:19:18: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
16 16 | f"{dict()}"
17 17 | f"a {dict()} b"
18 18 |
@@ -209,7 +209,7 @@ C408.py:20:5: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
17 17 | f"a {dict()} b"
18 18 |
19 19 | f"{dict(x='y') | dict(y='z')}"
@@ -228,7 +228,7 @@ C408.py:20:19: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
17 17 | f"a {dict()} b"
18 18 |
19 19 | f"{dict(x='y') | dict(y='z')}"
@@ -247,7 +247,7 @@ C408.py:21:6: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
18 18 |
19 19 | f"{dict(x='y') | dict(y='z')}"
20 20 | f"{ dict(x='y') | dict(y='z') }"
@@ -265,7 +265,7 @@ C408.py:21:20: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
18 18 |
19 19 | f"{dict(x='y') | dict(y='z')}"
20 20 | f"{ dict(x='y') | dict(y='z') }"
@@ -282,7 +282,7 @@ C408.py:22:7: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
19 19 | f"{dict(x='y') | dict(y='z')}"
20 20 | f"{ dict(x='y') | dict(y='z') }"
21 21 | f"a {dict(x='y') | dict(y='z')} b"
@@ -298,7 +298,7 @@ C408.py:22:21: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
19 19 | f"{dict(x='y') | dict(y='z')}"
20 20 | f"{ dict(x='y') | dict(y='z') }"
21 21 | f"a {dict(x='y') | dict(y='z')} b"

View File

@@ -10,7 +10,7 @@ C408.py:1:5: C408 [*] Unnecessary `tuple` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 |-t = tuple()
1 |+t = ()
2 2 | l = list()
@@ -27,7 +27,7 @@ C408.py:2:5: C408 [*] Unnecessary `list` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 1 | t = tuple()
2 |-l = list()
2 |+l = []
@@ -46,7 +46,7 @@ C408.py:3:6: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
1 1 | t = tuple()
2 2 | l = list()
3 |-d1 = dict()
@@ -65,7 +65,7 @@ C408.py:16:4: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
13 13 |
14 14 | f"{dict(x='y')}"
15 15 | f'{dict(x="y")}'
@@ -86,7 +86,7 @@ C408.py:17:6: C408 [*] Unnecessary `dict` call (rewrite as a literal)
|
= help: Rewrite as a literal
Suggested fix
Unsafe fix
14 14 | f"{dict(x='y')}"
15 15 | f'{dict(x="y")}'
16 16 | f"{dict()}"

View File

@@ -10,7 +10,7 @@ C409.py:1:6: C409 [*] Unnecessary `list` literal passed to `tuple()` (rewrite as
|
= help: Rewrite as a `tuple` literal
Suggested fix
Unsafe fix
1 |-t1 = tuple([])
1 |+t1 = ()
2 2 | t2 = tuple([1, 2])
@@ -27,7 +27,7 @@ C409.py:2:6: C409 [*] Unnecessary `list` literal passed to `tuple()` (rewrite as
|
= help: Rewrite as a `tuple` literal
Suggested fix
Unsafe fix
1 1 | t1 = tuple([])
2 |-t2 = tuple([1, 2])
2 |+t2 = (1, 2)
@@ -46,7 +46,7 @@ C409.py:3:6: C409 [*] Unnecessary `tuple` literal passed to `tuple()` (remove th
|
= help: Remove outer `tuple` call
Suggested fix
Unsafe fix
1 1 | t1 = tuple([])
2 2 | t2 = tuple([1, 2])
3 |-t3 = tuple((1, 2))
@@ -70,7 +70,7 @@ C409.py:4:6: C409 [*] Unnecessary `list` literal passed to `tuple()` (rewrite as
|
= help: Rewrite as a `tuple` literal
Suggested fix
Unsafe fix
1 1 | t1 = tuple([])
2 2 | t2 = tuple([1, 2])
3 3 | t3 = tuple((1, 2))
@@ -96,7 +96,7 @@ C409.py:8:6: C409 [*] Unnecessary `tuple` literal passed to `tuple()` (remove th
|
= help: Remove outer `tuple` call
Suggested fix
Unsafe fix
5 5 | 1,
6 6 | 2
7 7 | ])

View File

@@ -10,7 +10,7 @@ C410.py:1:6: C410 [*] Unnecessary `list` literal passed to `list()` (remove the
|
= help: Remove outer `list` call
Suggested fix
Unsafe fix
1 |-l1 = list([1, 2])
1 |+l1 = [1, 2]
2 2 | l2 = list((1, 2))
@@ -27,7 +27,7 @@ C410.py:2:6: C410 [*] Unnecessary `tuple` literal passed to `list()` (rewrite as
|
= help: Rewrite as a `list` literal
Suggested fix
Unsafe fix
1 1 | l1 = list([1, 2])
2 |-l2 = list((1, 2))
2 |+l2 = [1, 2]
@@ -44,7 +44,7 @@ C410.py:3:6: C410 [*] Unnecessary `list` literal passed to `list()` (remove the
|
= help: Remove outer `list` call
Suggested fix
Unsafe fix
1 1 | l1 = list([1, 2])
2 2 | l2 = list((1, 2))
3 |-l3 = list([])
@@ -60,7 +60,7 @@ C410.py:4:6: C410 [*] Unnecessary `tuple` literal passed to `list()` (rewrite as
|
= help: Rewrite as a `list` literal
Suggested fix
Unsafe fix
1 1 | l1 = list([1, 2])
2 2 | l2 = list((1, 2))
3 3 | l3 = list([])

View File

@@ -9,7 +9,7 @@ C411.py:2:1: C411 [*] Unnecessary `list` call (remove the outer call to `list()`
|
= help: Remove outer `list` call
Suggested fix
Unsafe fix
1 1 | x = [1, 2, 3]
2 |-list([i for i in x])
2 |+[i for i in x]

Some files were not shown because too many files have changed in this diff Show More