Compare commits

...

158 Commits

Author SHA1 Message Date
Carl Meyer
ab9b38c0f1 re-add para on CLICOLOR and CLICOLOR_FORCE 2024-04-05 16:07:54 -06:00
Carl Meyer
af12ee1763 Switch from colored to owo_colors/anstream 2024-04-04 17:55:43 -06:00
Alex Waygood
6b4fa17097 Rework docs for pydocstyle rules (#10754) 2024-04-03 22:34:00 +01:00
Carl Meyer
5e2482824c [flake8_comprehensions] add sum/min/max to unnecessary comprehension check (C419) (#10759)
Fixes #3259 

## Summary

Renames `UnnecessaryComprehensionAnyAll` to
`UnnecessaryComprehensionInCall` and extends the check to `sum`, `min`,
and `max`, in addition to `any` and `all`.

## Test Plan

Updated snapshot test.

Built docs locally and verified the docs for this rule still render
correctly.
2024-04-03 14:44:33 -06:00
Carl Meyer
e0a8fb607a fix obsolete name in resolve_qualified_name docs (#10762)
`resolve_call_path` was renamed to `resolve_qualified_name` in
a6d892b1f4, but the doc block for the
function wasn't updated to match.
2024-04-03 20:13:10 +00:00
Jane Lewis
257964a8bc ruff server now supports source.fixAll source action (#10597)
## Summary

`ruff server` now has source action `source.fixAll` as an available code
action.

This also fixes https://github.com/astral-sh/ruff/issues/10593 in the
process of revising the code for quick fix code actions.

## Test Plan




https://github.com/astral-sh/ruff/assets/19577865/f4c07425-e68a-445f-a4ed-949c9197a6be
2024-04-03 16:22:17 +00:00
Boshen
d467aa78c2 Remove an unused dependency (#10747)
## Summary

Continuation of #10475, I improved [`cargo
shear`](https://github.com/Boshen/cargo-shear) even more.

We can put this in CI once I test it a bit more, given that [ignoring
false
positives](https://github.com/Boshen/cargo-shear?tab=readme-ov-file#ignore-false-positives)
has been implemented.

## Test Plan

`cargo check --all-features --all-targets`
2024-04-03 09:57:19 +01:00
renovate[bot]
6e1c061e5f chore(deps): update rust crate lsp-types to v0.95.1 (#10686)
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [lsp-types](https://togithub.com/gluon-lang/lsp-types) |
workspace.dependencies | patch | `0.95.0` -> `0.95.1` |

---

### Release Notes

<details>
<summary>gluon-lang/lsp-types (lsp-types)</summary>

###
[`v0.95.1`](https://togithub.com/gluon-lang/lsp-types/blob/HEAD/CHANGELOG.md#v0951-2024-03-18)

[Compare
Source](https://togithub.com/gluon-lang/lsp-types/compare/v0.95.0...v0.95.1)

##### v0.95.1 (2024-03-18)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "on Monday" (UTC), Automerge - At any
time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/astral-sh/ruff).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-03 06:37:03 +00:00
Jane Lewis
9872f51293 Drop support for root_uri as an initialization parameter in ruff_server (#10743)
## Summary

Needed for https://github.com/astral-sh/ruff/pull/10686.

We no longer support `root_uri` as an initialization parameter, relying
solely on `workspace_folders` to find the working directories. This
means that the minimum supported LSP version is now `0.3.6`.

## Test Plan

When opening a folder in VS Code, you shouldn't see any errors in the
log which say `No workspace(s) were provided(...)`.
2024-04-02 20:51:59 -07:00
Charlie Marsh
e54b591ec7 Use section name range for all section-related docstring diagnostics (#10740)
## Summary

We may not have had access to this in the past, but in short, if the
diagnostic is related to a specific section of a docstring, it seems
better to highlight the section (via the header) than the _entire_
docstring.

This should be completely compatible with existing `# noqa` since it's
always inside of a multi-line string anyway, and in such cases the `#
noqa` is always placed at the end of the multiline string.

Closes https://github.com/astral-sh/ruff/issues/10736.
2024-04-02 22:10:04 -04:00
Carl Meyer
814b26f82e [flake8_comprehensions] update docs for unnecessary-comprehension-any-all (C419) (#10744)
Ref #3259; see in particular
https://github.com/astral-sh/ruff/issues/3259#issuecomment-2033127339

## Summary

Improve the accuracy of the docs for this lint rule/fix.

## Test Plan

Generated the docs locally and visited the page for this rule:

![Screenshot 2024-04-02 at 4 56
40 PM](https://github.com/astral-sh/ruff/assets/61586/64f25cf6-edfe-4682-ac8e-7e21b834f5f2)

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
2024-04-02 17:42:55 -06:00
Carl Meyer
2a4084a2bb chore(docs): update ruff_linter crate name in CONTRIBUTING.md (#10745)
Reading through `CONTRIBUTING.md`, I happened to notice that it still
referred to the `ruff_linter` crate as the `ruff` crate. `ruff` is a
different crate, located in `crates/ruff`, which doesn't contain "the
vast majority of the code and all the lint rules."
2024-04-02 17:06:21 -06:00
Charlie Marsh
dff8f93457 [flake8-return] Ignore assignments to annotated variables in unnecessary-assign (#10741)
## Summary

Closes https://github.com/astral-sh/ruff/issues/10732.
2024-04-02 16:18:05 -04:00
Aleksei Latyshev
859e3fc7fa [refurb] Implement int-on-sliced-str (FURB166) (#10650)
## Summary
implement int_on_sliced_str (FURB166) lint
- #1348
- [original
lint](https://github.com/dosisod/refurb/blob/master/refurb/checks/builtin/use_int_base_zero.py)

## Test Plan
cargo test
2024-04-02 19:29:42 +00:00
Aleksei Latyshev
0de23760ff [pylint] Don't recommend decorating staticmethods with @singledispatch (PLE1519, PLE1520) (#10637)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-04-02 16:47:31 +00:00
renovate[bot]
b90e6df5cc chore(deps): update rust crate env_logger to 0.11.0 (#10700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-02 15:03:54 +02:00
Micha Reiser
0d20ec968f Build codspeed benchmarks by calling cargo directly (#10735) 2024-04-02 14:50:19 +02:00
Dhruv Manilawala
99dd3a8ab0 Implement as_str & Display for all operator enums (#10691)
## Summary

This PR adds the `as_str` implementation for all the operator methods.
It already exists for `CmpOp` which is being [used in the
linter](ffcd77860c/crates/ruff_linter/src/rules/flake8_simplify/rules/key_in_dict.rs (L117))
and it makes sense to implement it for the rest as well. This will also
be utilized in error messages for the new parser.
2024-04-02 10:34:36 +00:00
Dhruv Manilawala
eee2d5b915 Remove unused operator methods and impl (#10690)
## Summary

This PR removes unused operator methods and impl traits. There is
already the `is_macro::Is` implementation for all the operators and this
seems unnecessary.
2024-04-02 15:53:20 +05:30
Charlie Marsh
159bad73d5 Accept non-aliased (but correct) import in unconventional-import-alias (#10729)
## Summary

Given:

```toml
[tool.ruff.lint.flake8-import-conventions.aliases]
"django.conf.settings" = "settings"
```

We should accept `from django.conf import settings`.

Closes https://github.com/astral-sh/ruff/issues/10599.
2024-04-01 23:47:20 -04:00
Charlie Marsh
7b48443624 Respect Q00* ignores in flake8-quotes rules (#10728)
## Summary

We lost the per-rule ignores when these were migrated to the AST, so if
_any_ `Q` rule is enabled, they're now all enabled.

Closes https://github.com/astral-sh/ruff/issues/10724.

## Test Plan

Ran:

```shell
ruff check . --isolated --select Q --ignore Q000
ruff check . --isolated --select Q --ignore Q001
ruff check . --isolated --select Q --ignore Q002
ruff check . --isolated --select Q --ignore Q000,Q001
ruff check . --isolated --select Q --ignore Q000,Q002
ruff check . --isolated --select Q --ignore Q001,Q002
```

...against:

```python
'''
bad docsting
'''
a = 'single'
b = '''
bad multi line
'''
```
2024-04-02 03:21:12 +00:00
Charlie Marsh
d36f60999d Ignore annotated lambdas in class scopes (#10720)
## Summary

An annotated lambda assignment within a class scope is often
intentional. For example, within a dataclass or Pydantic model, these
are treated as fields rather than methods (and so can be passed values
in constructors).

I originally wrote this to special-case dataclasses and Pydantic
models... But was left feeling like we'd see more false positives here
for little gain (an annotated lambda within a `class` is likely
intentional?). Open to opinions, though.

Closes https://github.com/astral-sh/ruff/issues/10718.
2024-04-01 15:44:45 -04:00
Charlie Marsh
67f0f615b2 Recursively resolve TypeDicts for N815 violations (#10719)
## Summary

Only works within a single file for now.

Closes https://github.com/astral-sh/ruff/issues/10671.
2024-04-01 17:40:55 +00:00
Charlie Marsh
200ebeebdc Bump version to v0.3.5 (#10717) 2024-04-01 13:15:32 -04:00
renovate[bot]
23e8279093 chore(deps): update npm development dependencies (#10716) 2024-04-01 16:10:33 +00:00
renovate[bot]
221b3236a8 chore(deps): update strum to 0.26.0 (#10715) 2024-04-01 16:06:51 +00:00
renovate[bot]
a0e1544848 chore(deps): update rust crate pep440_rs to 0.5.0 (#10703) 2024-04-01 16:02:18 +00:00
Alex Waygood
2740fab7ad Renovate: group all strum dependencies together (#10714) 2024-04-01 15:57:07 +00:00
renovate[bot]
7042b9b16d fix(deps): update rust crate similar to v2.5.0 (#10711) 2024-04-01 11:54:58 -04:00
renovate[bot]
4047d456b6 chore(deps): update rust crate insta to v1.38.0 (#10701) 2024-04-01 15:44:30 +00:00
renovate[bot]
20d69ea504 chore(deps): update npm development dependencies (#10697) 2024-04-01 10:45:07 -04:00
renovate[bot]
d021cac0c9 chore(deps): update rust crate tracing-tree to 0.3.0 (#10709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-01 14:21:50 +01:00
renovate[bot]
46369d48fe chore(deps): update rust crate uuid to v1.8.0 (#10710)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-01 14:20:05 +01:00
renovate[bot]
85d59198aa chore(deps): update tj-actions/changed-files action to v44 (#10712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-01 14:19:28 +01:00
renovate[bot]
20a2e25cb0 chore(deps): update rust crate serde_with to v3.7.0 (#10706) 2024-04-01 12:37:41 +00:00
renovate[bot]
a32e70d449 chore(deps): update rust crate insta-cmd to 0.5.0 (#10702) 2024-04-01 11:55:05 +00:00
renovate[bot]
76d0edbbaa chore(deps): update rust crate toml to v0.8.12 (#10696) 2024-04-01 11:53:43 +00:00
Alex Waygood
8d547ef83a Allow renovate to create more PRs at once (#10695) 2024-04-01 11:43:07 +00:00
renovate[bot]
d7a6978e05 chore(deps): update rust crate syn to v2.0.57 (#10694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-01 11:24:48 +00:00
renovate[bot]
786ff403b1 chore(deps): update rust crate serde_json to v1.0.115 (#10693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-01 11:24:15 +00:00
Alex Waygood
2ea0c3dce6 Give renovate more time in which to file PRs (#10692) 2024-04-01 11:13:14 +00:00
renovate[bot]
b9dfa7845f chore(deps): update rust crate memchr to v2.7.2 (#10688) 2024-04-01 08:25:05 +01:00
renovate[bot]
090f6580d3 Update rust crate regex to v1.10.4 (#10689) 2024-04-01 08:24:13 +01:00
renovate[bot]
eb884c8f76 chore(deps): update rust crate indoc to v2.0.5 (#10685) 2024-04-01 01:10:27 +00:00
Steve C
f6b6f0df67 [ruff] Fix panic in unused # noqa removal with multi-byte space (RUF100) (#10682)
## Summary

Currently, [this
line](716688d44e/crates/ruff_linter/src/fix/edits.rs (L101))
assumes that the `noqa` comment begins with an octothorpe followed by a
space. (`# `) With anyone's random code, this of course is not always
true.

When there's a multi-byte character after the leading octothorpe, such
as
[`\u0085`](https://www.fileformat.info/info/unicode/char/85/index.htm),
we try slicing from within the character, causing a panic.

To fix this, the logic has been changed to remove unused `noqa`
directives and keep any trailing comments, or removing the whole comment
if the comment is just the unused `noqa`

Fixes #10097.

## Test Plan

`cargo test`
2024-04-01 01:00:37 +00:00
renovate[bot]
9f2127bf04 Update rust crate clap to v4.5.4 (#10684) 2024-04-01 01:16:02 +01:00
renovate[bot]
8cd096d9b5 chore(deps): update rust crate chrono to v0.4.37 (#10683) 2024-04-01 01:15:24 +01:00
hikaru-kajita
716688d44e [pylint] Implement modified-iterating-set (E4703) (#10473)
## Summary

Implement `E4703` in the issue #970.
Relevant pylint docs is here:
https://pylint.readthedocs.io/en/stable/user_guide/messages/error/modified-iterating-set.html

## Test Plan

I've written it in the `modified_iterating_set.py`.
2024-03-31 14:37:49 +00:00
hikaru-kajita
9ad9cea952 [refurb] Implement unnecessary-from-float (FURB164) (#10647)
## Summary

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

Implement FURB164 in the issue #1348.
Relevant Refurb docs is here:
https://github.com/dosisod/refurb/blob/v2.0.0/docs/checks.md#furb164-no-from-float

I've changed the name from `no-from-float` to
`verbose-decimal-fraction-construction`.

## Test Plan

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

I've written it in the `FURB164.py`.

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
2024-03-30 07:04:01 -04:00
Charlie Marsh
75e01420fa Always place non-relative imports after relative imports (#10669)
## Summary

When `relative-imports-order = "closest-to-furthest"` is set, we should
_still_ put non-relative imports after relative imports. It's rare for
them to be in the same section, but _possible_ if you use
`known-local-folder`.

Closes https://github.com/astral-sh/ruff/issues/10655.

## Test Plan

New tests.

Also sorted this file:

```python
from ..models import ABC
from .models import Question
from .utils import create_question
from django_polls.apps.polls.models import Choice
```

With both:

- `isort view.py`
- `ruff check view.py --select I --fix`

And the following `pyproject.toml`:

```toml
[tool.ruff.lint.isort]
order-by-type = false
relative-imports-order = "closest-to-furthest"
known-local-folder = ["django_polls"]

[tool.isort]
profile = "black"
reverse_relative = true
known_local_folder = ["django_polls"]
```

I verified that Ruff and isort gave the same result, and that they
_still_ gave the same result when removing the relevant setting:

```toml
[tool.ruff.lint.isort]
order-by-type = false
known-local-folder = ["django_polls"]

[tool.isort]
profile = "black"
known_local_folder = ["django_polls"]
```
2024-03-29 22:13:54 -04:00
Charlie Marsh
fc54f53662 Make unnecessary-lambda an always-unsafe fix (#10668)
## Summary

See the linked issue and comment for more.

Closes https://github.com/astral-sh/ruff/issues/10663.
2024-03-30 01:05:05 +00:00
Charlie Marsh
7c2e9f71ea Change quadratic-list-summation docs to use iadd consistently (#10666)
Closes https://github.com/astral-sh/ruff/issues/10088.
2024-03-30 00:32:34 +00:00
Auguste Lalande
3c48913473 [flake8-boolean-trap] Add setting for user defined allowed boolean trap (#10531)
## Summary

Add a setting `extend-allowed-calls` to allow users to define their own
list of calls which allow boolean traps.

Resolves #10485.
Resolves #10356.

## Test Plan

Extended text fixture and added setting test.
2024-03-30 00:26:12 +00:00
Charlie Marsh
9f56902719 Add PR title format to CONTRIBUTING.md (#10665)
Closes https://github.com/astral-sh/ruff/issues/9470.
2024-03-30 00:14:16 +00:00
Dhruv Manilawala
1bcdfe268d Allow f-strings with %z for DTZ007 (#10651)
## Summary

This PR fixes the bug for `DTZ007` rule where it didn't consider to
check for the presence of `%z` in f-strings. It also considers the
string parts of an implicitly concatenated f-strings for which I want to
find a better solution (#10308).

fixes: #10601 

## Test Plan

Add test cases and update the snapshots.
2024-03-29 09:57:13 +05:30
Mateusz Sokół
a0263ab472 Add row_stack to NumPy 2.0 migration rule (#10646)
Hi! 

I left out one of the functions in the migration rule which became
deprecated/removed in NumPy 2.0
(https://github.com/numpy/numpy/issues/26032#issuecomment-1999870797).

cc @anntzer
2024-03-28 09:49:40 -04:00
Jane Lewis
6b580c1544 Support unused code formatting for ruff server (#10644)
## Summary

Fixes #10589.

Code that violates `F401` or `F841` (in other words, unused variables or
imports) should now appear greyed out or 'unused' in an editor.

## Test Plan

Put the following test code in a new file within the extension
development host window:

```python
import math
def func():
   if False:
      unused = "<- this should be greyed out"
```

The following test code should have greyed out/unused import and
variable names, like so:
<img width="294" alt="Screenshot 2024-03-28 at 4 23 18 AM"
src="https://github.com/astral-sh/ruff/assets/19577865/e84a6e7a-49e2-4fed-9624-f8f9559e0837">
2024-03-28 11:30:35 +00:00
Jane Lewis
3f7d666e8b ruff server now highlights most issues as warnings (#10643)
## Summary

Fixes #10588.

Most diagnostics from `ruff server` now appear as a much less alarming
warning instead of an error. Three diagnostics still appear as errors:
`F821` (`undefined name <name>`), `E902` (`IOError`) and `E999`
(`SyntaxError`).

## Test Plan

With an extension using the path to a locally-built executable, open a
file with multiple highlighted problems. Toggle the `Experimental
Server` setting on and off. The highlights should stay as warnings.

Then, modify the file to have a syntactically incorrect element. The
start of the invalid syntax should now have a red highlight.
2024-03-28 04:14:17 -07:00
Mix
cce25ec116 Update warning message for rule S305 to address insecure block cipher mode use (#10602)
<!--
Thank you for contributing to Ruff! To help us out with reviewing,
please consider the following:

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

## Summary

This PR updates the warning message for rule S305 to accurately reflect
the security concern over using ECB mode in block ciphers, which is
considered insecure compared to other modes like CBC or CTR. The
previous message incorrectly mentioned AES as a [block cipher
mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation),
which has been corrected to avoid confusion.

Ref: 

c85576d903/bandit/blacklists/calls.py (L99-L102)


825fd7c990/crates/ruff_linter/src/rules/flake8_bandit/rules/suspicious_function_call.rs (L187-L216)

## Test Plan

No testing required as the change is limited to a minor change of
warning message update.
2024-03-27 21:00:49 -04:00
Peter A. Jonsson
fc7fa59e5f space_around_operator: use same before/after numbers (#10640)
## Summary

The example for tab-after-comma (E242):
```python
a = 4,\t5
```
Use instead:
```python
a = 4, 3
```
is confusing since both the whitespace and the numbers are changed.

Change so the examples use the same numbers before/after.

## Test Plan

Untested.
2024-03-27 19:31:19 -04:00
Alex Waygood
abbefae6f1 DTZ rules: Clarify error messages and docs (#10621)
- Clearly state in the documentation that passing `tz=None` is just as bad as not passing a `tz=` argument, from the perspective of these rules.
- Clearly state in the error messages exactly what the user is doing wrong, if the user is passing `tz=None` rather than failing to pass a `tz=` argument at all.
- Make error messages more concise, and separate out the suggested remedy from the thing that the user is identified as doing wrong.

Co-authored-by: Christian Clauss <cclauss@me.com>
2024-03-27 19:42:13 +00:00
Aleksei Latyshev
f9d0c6d9ae [refurb] Implement for-loop-set-mutations (FURB142) (#10583)
Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
2024-03-27 09:26:12 +01:00
Jane Lewis
4d59142255 Client request sender and inbound response handling for ruff server (#10620)
## Summary

Fixes #10618.

This PR introduces a proper API for sending requests to the client and
handling any response sent back. Dynamic capability registration now
uses this new API, fixing an issue where a much more simplistic response
handler silently flushes a code action request that needed a response.

## Test Plan

#10618 can no longer be reproduced. No errors about unhandled responses
should appear in the extension output, and you should see this new log
when the server starts:
```
<DATE> <TIME> [info] <DURATION> INFO ruff_server::server Configuration file watcher successfully registered
```
2024-03-26 13:53:56 -07:00
renovate[bot]
72aa1ce00f Update dependency uuid to v9.0.1 (#10615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 12:05:17 +00:00
renovate[bot]
b6b737c937 Update dependency @miniflare/storage-memory to v2.14.2 (#10614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 12:04:59 +00:00
renovate[bot]
b074e7dc9b Update dependency @miniflare/kv to v2.14.2 (#10613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 12:03:41 +00:00
renovate[bot]
e81f1f7971 Update NPM Development dependencies (#10610)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 12:53:29 +01:00
renovate[bot]
83c3580346 Update dependency react-resizable-panels to v2 (#10611)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-03-26 11:52:25 +00:00
renovate[bot]
f0662eea48 Update Monaco (#10609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-03-26 11:41:21 +00:00
Micha Reiser
b49b861b2d Delete unused Violation::explanation method (#10600) 2024-03-26 12:35:27 +01:00
Micha Reiser
877a9145ae Group some NPM dependency updates (#10607) 2024-03-26 11:39:56 +01:00
renovate[bot]
ba24bd88cf Update dependency classnames to v2.5.1 (#10603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-26 09:01:35 +00:00
Jane Lewis
825fd7c990 High-level project overview and contributing guide for ruff server (#10565)
## Summary

This PR adds an overview and roadmap to the `README.md` for the
`ruff_server` crate along with a rudimentary `CONTRIBUTING.md` that
explains some of the technical decisions behind the project and basic
information about local testing.
2024-03-25 23:08:37 -07:00
hikaru-kajita
a28776e3aa [flake8-comprehensions] Handled special case for C401 which also matches C416 (#10596)
## Summary

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

Similar to #10419, there was a case where there is a collision of C401
and C416 (as discussed in #10101).
Fixed this by implementing short-circuit for the comprehension of the
form `{x for x in foo}`.

## Test Plan

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

Extended `C401.py` with the case where `set` is not builtin function,
and divided the case where the short-circuit should occur.
Removed the last testcase of `print(f"{ {set(a for a in 'abc')} }")`
test as this is invalid as a python code, but should I keep this?
2024-03-26 03:54:58 +00:00
Aurelio Jargas
80b46889ed Fix list markup: blank line required (#10591)
Judging from the other lists in this same file, it looks like a blank
line is required to separate the previous paragraph from the first list
item.

Otherwise the list is considered part of the paragraph:

- https://docs.astral.sh/ruff/settings/#lint_flake8-copyright_notice-rgx

<img width="400" alt="image"
src="https://github.com/astral-sh/ruff/assets/282592/663fa5b4-f787-4fcd-bdd6-0b891d7ad72b">

- https://docs.astral.sh/ruff/settings/#format_quote-style

<img width="400" alt="image"
src="https://github.com/astral-sh/ruff/assets/282592/809735eb-c546-4348-9311-877441bae84e">

After this change hopefully those will be rendered as proper bullet
lists.
2024-03-26 00:18:35 +00:00
renovate[bot]
fdd25f0d99 Update dependency vite to v4.5.2 (#10576) 2024-03-25 17:50:41 -05:00
renovate[bot]
e0ab5629cc Update dependency wrangler to v2.20.2 (#10577) 2024-03-25 17:50:23 -05:00
Filipe Laíns
960e47423c Put flake8-logging next to the other flake8 plugins in registry (#10587)
## Summary

This is just a nitpicky improvement, but I thought it'd be a good
opportunity to look at the ruff source.

> The rules list in the documentation is generated using the registry
order. Currently, flake8-logging is separated from the rest of the
flake8 plugins. This patch puts it next to them.

https://docs.astral.sh/ruff/rules/

If it makes sense, we could alternatively just sort the linters in
https://github.com/astral-sh/ruff/blob/main/crates/ruff_dev/src/generate_rules_table.rs.

Signed-off-by: Filipe Laíns <lains@riseup.net>
2024-03-25 20:23:31 +00:00
Dhruv Manilawala
4950ca4142 Ignore Q000, Q001 when string is inside forward ref (#10585)
## Summary

This is not the holistic solution but just to fix that issue.

fixes: #10546 

## Test Plan

Add a regression test for it and check the snapshots.
2024-03-25 18:52:59 +00:00
renovate[bot]
3e1f3b8132 Update Rust crate anyhow to v1.0.81 (#10584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-03-25 18:28:02 +00:00
Alex Waygood
21f63c57d5 Renovate: Use update-lockfile as the rangeStrategy for cargo dependencies (#10582) 2024-03-25 18:17:03 +00:00
Alex Waygood
bd07c13348 Get rid of SCARY SECURITY suffixes for security-related renovate PRs (#10579) 2024-03-25 17:57:12 +00:00
Alex Waygood
9e21e5918c Switch from dependabot to renovate (#10567) 2024-03-25 17:30:11 +00:00
hikaru-kajita
f7aab5ac69 [pylint] Fixed false-positive on the rule PLW1641 (eq-without-hash) (#10566)
## Summary

Fixed false-positive on the rule `PLW1641`, where the explicit
assignment on the `__hash__` method is not counted as an definition of
`__hash__`. (Discussed in #10557).

Also, added one new testcase.

## Test Plan

Checked on `cargo test` in `eq_without_hash.py`.

Before the change, for the assignment into `__hash__`, only `__hash__ =
None` was counted as an explicit definition of `__hash__` method.
Probably any assignment into `__hash__` property could be counted as an
explicit definition of hash, so I removed `value.is_none_literal_expr()`
check.
2024-03-25 14:40:01 +00:00
Alex Waygood
59ac3f48c8 Select more rules for typeshed in the ecosystem checks (#10564) 2024-03-25 14:04:11 +00:00
Hoël Bagard
9512bd66b5 [pycodestyle] Avoid blank line rules for the first logical line in cell (#10291)
## Summary

Closes #10228

The PR makes the blank lines rules keep track of the cell status when
running on a notebook, and makes the rules not trigger when the line is
the first of the cell.

## Test Plan

The example given in #10228 is added as a fixture, along with a few
tests from the main blank lines fixtures.
2024-03-25 11:19:30 +00:00
dependabot[bot]
5729dc3589 Bump syn from 2.0.52 to 2.0.55 (#10563)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 10:49:48 +01:00
dependabot[bot]
2890485785 Bump aho-corasick from 1.1.2 to 1.1.3 (#10560)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 09:27:07 +00:00
dependabot[bot]
a5f6e5dc88 Bump rayon from 1.9.0 to 1.10.0 (#10559)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 10:16:52 +01:00
dependabot[bot]
d93db63a22 Bump smallvec from 1.13.1 to 1.13.2 (#10562)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 10:16:08 +01:00
dependabot[bot]
ecc11dcc12 Bump bitflags from 2.4.2 to 2.5.0 (#10561)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-25 10:14:39 +01:00
Dhruv Manilawala
e9115b8d8a Move Q004 to AST based checker (#10548)
## Summary

Continuing with #7595, this PR moves the `Q004` rule to the AST checker.

## Test Plan

- [x] Existing test cases should pass
- [x] No ecosystem updates
2024-03-25 03:31:31 +00:00
Alexey Preobrazhenskiy
d625f55c05 Nested namespace packages support (#10541)
## Summary
PEP 420 says [nested namespace
packages](https://peps.python.org/pep-0420/#nested-namespace-packages)
are allowed, i.e. marking a directory as a namespace package marks all
subdirectories in the subtree as namespace packages.

`is_package` is modified to use `Path::starts_with` and the order of
checks is reversed to do in-memory checks first before hitting the disk.

## Test Plan
Added unit tests. Previously all tests were run with `namespace_packages
== &[]`. Verified that one of the tests was failing before changing the
implementation.

## Future Improvements
The `is_package_with_cache` can probably be rewritten to avoid repeated
calls to `Path::starts_with`, by caching all directories up to the
`namespace_root`:
```ruff
let namespace_root = namespace_packages
    .iter()
    .filter(|namespace_package| path.starts_with(namespace_package))
    .min();
```
2024-03-24 22:53:32 -04:00
Charlie Marsh
9856c1446b Document use of anonymous assignment in useless-expression (#10551)
Closes https://github.com/astral-sh/ruff/issues/10536.
2024-03-25 02:46:33 +00:00
hikaru-kajita
39fb6d9bfc [refurb] Implement verbose-decimal-constructor (FURB157) (#10533)
## Summary

Implement FURB157 in the issue #1348.
Relevant Refurb docs is here:
https://github.com/dosisod/refurb/blob/master/docs/checks.md#furb157-simplify-decimal-ctor

## Test Plan

I've written it in the `FURB157.py`.
2024-03-24 22:28:58 -04:00
yt2b
22f237fec6 [flake8-bugbear] Avoid false positive for usage after continue (B031) (#10539)
## Summary

Closes #10337.

I've fixed the code to count usage of variable.
Usage count inside the block is reset when there is a following
statement.
- continue
- break
- return 

## Test Plan

Add test case.
2024-03-25 00:38:30 +00:00
Alex Waygood
021f0bdccb Mark PYI025 fix as safe in more cases for stub files (#10547)
## Summary

The fix for PYI025 is currently marked as unsafe in non-global scopes
for both `.py` and `.pyi` files, on the grounds that all global-scope
symbols in Python are implicitly exported from the module, so changing
the name of something in the global scope could break other modules that
import the module we're fixing. Unlike in `.py` files, however, imported
symbols are never implicitly re-exported from stub files. Symbols are
only understood by static analysis tools as being re-exported from stubs
if they are marked as explicit re-exports, which take three forms:

```py
from foo import *  # all symbols from foo are re-exported from the stub

# the "redundant" alias marks it as an explicit re-export
# (note that the alias needs to be identical to the symbol's "actual" name
# in order for it to be a re-export)
from bar import barrr as barrr

# inclusion in __all__ also marks it as an explicit re-export,
# just like in `.py` files
from baz import bazzz
__all__ = ["bazzz"]
```

This is [specc'd in PEP
484](https://peps.python.org/pep-0484/#stub-files), and means that we
can mark the fix for PYI025 as safe in more cases for `.pyi` files.

## Test Plan

`cargo test`. An existing test case goes from being an unsafe fix to a
safe fix in a `.pyi` fixture. I also added a new fixture so we have
coverage of global-scope imports that are marked as re-exports using
"redundant" `from collections.abc import Set as Set` aliases.
2024-03-24 16:11:48 +00:00
Alexey Preobrazhenskiy
7cc40d5621 Refactor package_roots for readability. (#10543) 2024-03-24 12:02:30 +00:00
Dhruv Manilawala
c447454111 [E402] Allow cell magics before an import (#10545) 2024-03-24 16:20:00 +05:30
Dhruv Manilawala
895d9df02f Move Q001-3 to AST based checker (#10312)
## Summary

Continuing with https://github.com/astral-sh/ruff/issues/7595, this PR
moves the `Q001`, `Q002`, `Q003` rules to the AST based checker.

## Test Plan

Make sure all of the existing test cases pass and verify there are no
ecosystem changes.
2024-03-23 22:59:50 +05:30
Auguste Lalande
0c194f55e8 Fix PT014 autofix for last item in list (#10532)
## Summary

This error was found browsing
https://github.com/qarmin/Automated-Fuzzer/actions/runs/8396966850.
Which failed when trying to autofix the PT014 violation in the following
code:
```python
@pytest.mark.parametrize('data, spec', [(1.0, 1.0), (1.0, 1.0)])
def test_numbers(data, spec):
    ...
```

Investigation revealed that the implementation was not properly tested,
when the duplicate value was also the last in the list. In particular
the following function, which is in charge of finding the comma
following an element to create the suggested fix,

0a99bd84ce/crates/ruff_linter/src/rules/flake8_pytest_style/rules/parametrize.rs (L647-L651)
would find the next comma even if it was outside the list itself leading
to a lot of code being deleted.

This PR fixes that.

## Test Plan

Added misbehaving code to the test fixture.
2024-03-23 09:26:42 -04:00
Zanie Blue
0a99bd84ce Add PlasmaPy to ecosystem checks (#10530)
ref
https://github.com/astral-sh/ruff/issues/10528#issuecomment-2015805710
2024-03-22 15:28:13 -05:00
Alex Waygood
9feb9b0aa8 Correctly handle references in __all__ definitions when renaming symbols in autofixes (#10527) 2024-03-22 20:06:35 +00:00
Charlie Marsh
61b7982422 Respect Unicode characters in import sorting (#10529)
## Summary

Ensures that we use the raw identifier as provided in the source code,
rather than the normalized Unicode identifier.

This _does_ mean that we treat these as two separate identifiers, and
_don't_ merge them, even though Python will treat them as the same
symbol:

```python
import numpy as ℂℇℊℋℌℍℎℐℑℒℓℕℤΩℨKÅℬℭℯℰℱℹℴ
import numpy as CƐgHHHhIILlNZΩZKÅBCeEFio
```

I think that's fine, this is super rare anyway and would likely be
confusing for users.

Closes https://github.com/astral-sh/ruff/issues/10528.

## Test Plan

`cargo test`
2024-03-22 15:16:49 -04:00
Ryan May
594b232e0f Accept commas in default copyright pattern (#9498)
## Summary

Adds commas as an accepted separator between copyright years by default,
which is actually documented in one spot, but not currently accurate.
Fixes #9477.
2024-03-22 14:42:02 -04:00
Alex Waygood
a06ffeb54e Track ranges of names inside __all__ definitions (#10525) 2024-03-22 18:38:40 +00:00
Alex Waygood
b74dd420fc Fix F821 false negatives when from __future__ import annotations is active (attempt 2) (#10524) 2024-03-22 18:11:16 +00:00
Jane Lewis
4f06d59ff6 Automatic configuration reloading for ruff server (#10404)
## Summary

Fixes #10366.

`ruff server` now registers a file watcher on the client side using the
LSP protocol, and listen for events on configuration files. On such an
event, it reloads the configuration in the 'nearest' workspace to the
file that was changed.

## Test Plan

N/A
2024-03-21 20:17:07 +00:00
Charlie Marsh
5062572aca Bump version to v0.3.4 (#10515) 2024-03-21 18:08:21 +00:00
Charlie Marsh
dc6f6398e7 Rename list-reassign-reversed to list-reverse-copy (#10514)
After discussion with @MichaReiser.
2024-03-21 17:33:00 +00:00
Aleksei Latyshev
01fe268612 [refurb] Implement list_assign_reversed lint (FURB187) (#10212)
## Summary

Implement [use_reverse
(FURB187)](https://github.com/dosisod/refurb/blob/master/refurb/checks/readability/use_reverse.py)
lint.
Tests were copied from original
https://github.com/dosisod/refurb/blob/master/test/data/err_187.py.

## Test Plan

cargo test
2024-03-21 17:09:09 +00:00
Alex Waygood
c62184d057 'Revert "F821: Fix false negatives in .py files when from __future__ import annotations is active (#10362)"' (#10513) 2024-03-21 16:41:05 +00:00
Auguste Lalande
9b3c732538 Docs: Link inline settings when not part of options section (#10499)
## Summary
 
Some contributors have referenced settings in their documentation
without adding the settings to an options section, this has lead to some
rendering issues (#10427). This PR addresses this looking for potential
inline links to settings, cross-checking them with the options sections,
and then linking them anyway if they are not found.

Resolves #10427.

## Test Plan

Manually verified that the correct modifications were made and no docs
were broken.
2024-03-21 16:33:58 +00:00
Charlie Marsh
caa1450895 Don't treat annotations as redefinitions in .pyi files (#10512)
## Summary

In https://github.com/astral-sh/ruff/pull/10341, we fixed some false
positives in `.pyi` files, but introduced others. This PR effectively
reverts the change in #10341 and fixes it in a slightly different way.
Instead of changing the _bindings_ we generate in the semantic model in
`.pyi` files, we instead change how we _resolve_ them.

Closes https://github.com/astral-sh/ruff/issues/10509.
2024-03-21 12:22:50 -04:00
Charlie Marsh
60fd98eb2f Update Rust to v1.77 (#10510) 2024-03-21 12:10:33 -04:00
Alex Waygood
ac150b9314 Spruce up docs for flake8-pyi rules (part 2) (#10494)
- Improve clarity over the motivation for some rules
- Improve links to external references. In particular, reduce links to PEPs, as PEPs are generally historical documents rather than pieces of living documentation. Where possible, it's better to link to the official typing spec, the other docs at typing.readthedocs.io/en/latest, or the docs at docs.python.org/3/library/typing.html.
- Use more concise language in a few places
2024-03-21 11:54:43 +00:00
Auguste Lalande
d9ac170eb4 Fix E231 bug: Inconsistent catch compared to pycodestyle, such as when dict nested in list (#10469)
<!--
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

Fix `E231` bug: Inconsistent catch compared to pycodestyle, such as when
dict nested in list. Resolves #10113.

## Test Plan

Example from #10113 added to test fixture.
2024-03-21 09:13:37 +01:00
veryyet
c5ea4209bb chore: remove repetitive words (#10502) 2024-03-21 03:57:16 +00:00
Auguste Lalande
4ad3166a3f Update number of implemented rules to "over 800" (#10500)
## Summary

Ruff now implements over 800 rules. Congrats everyone 🎉

819 by my count
2024-03-20 22:39:37 -04:00
Charlie Marsh
9aded0284e Add missing Options references to blank line docs (#10498)
See: https://github.com/astral-sh/ruff/issues/10427.
2024-03-21 00:49:36 +00:00
Auguste Lalande
a5f41e8d63 Add a formatting step using mdformat as part of generate_mkdocs.py (#10484)
## Summary

The purpose of this change is mainly to address one of the issues
outlined in #10427. Namely, some lists in the docs were not rendering
properly when preceded by a text block without a newline character. This
PR adds `mdformat` as a final step to the rule documentation script, so
that any missing newlines will be added.

NB: The default behavior of `mdformat` is to escape markdown special
characters found in text such as `<`. This resulted in some misformatted
docs. To address this I implemented an ad-hoc mdformat plugin to
override the behavior. This may be considered a bit 'hacky', but I think
it's a good solution. Nevertheless, if someone has a better idea, let me
know.

## Test Plan

This change is hard to test systematically, however, I tried my best to
look at the before and after diffs to ensure no unwanted changes were
made to the docs.
2024-03-21 00:37:40 +00:00
Auguste Lalande
685de912ff [pylint] Implement nan-comparison (PLW0117) (#10401)
## Summary

Implement pylint's nan-comparison, part of #970.

## Test Plan

Text fixture was added.
2024-03-21 00:36:17 +00:00
Sergey Chudov
4045df4ad4 Avoid incorrect tuple transformation in single-element case (C409) (#10491)
# Summary
Fixed: incorrect rule transformation rule C409 with single element.

# Test Plan
Added examples from #10323 to test fixtures.
2024-03-21 00:09:28 +00:00
Micha Reiser
9d705a4414 Fix subscript comment placement with parenthesized value (#10496)
## Summary

This is a follow up on https://github.com/astral-sh/ruff/pull/10492 

I incorrectly assumed that `subscript.value.end()` always points past
the value. However, this isn't the case for parenthesized values where
the end "ends" before the parentheses.

## Test Plan

I added new tests for the parenthesized case.
2024-03-20 20:30:22 +00:00
Auguste Lalande
fd3d272026 Improve clarity of PT006's error message (#10468)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
2024-03-20 18:22:02 +00:00
Micha Reiser
954a48b129 Fix instable formatting for trailing subscribt end-of-line comment (#10492)
## Summary

This PR fixes an instability where formatting a subscribt 
where the `slice` is not an `ExprSlice` and it has a trailing
end-of-line comment after its opening `[` required two formatting passes
to be stable.

The fix is to associate the trailing end-of-line comment as dangling
comment on `[` to preserve its position, similar to how Ruff does it for
other parenthesized expressions.
This also matches how trailing end-of-line subscript comments are
handled when the `slice` is an `ExprSlice`.

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

## Versioning

Shipping this as part of a patch release is fine because:

* It fixes a stability issue
* It doesn't impact already formatted code because Ruff would already
have moved to the comment to the end of the line (instead of preserving
it)

## Test Plan

Added tests
2024-03-20 18:12:10 +01:00
Alex Waygood
7caf0d064a Simplify formatting of strings by using flags from the AST nodes (#10489) 2024-03-20 16:16:54 +00:00
Vasco Schiavo
fc792d1d2e Fix error message for rule C400 (#10488)
Fix a typos in the error message of rule C400

With the latest version of Ruff (0.3.3) if I have a `scratch.py` script
like that:

```python
from typing import Dict, List, Tuple


def generate_samples(test_cases: Dict) -> List[Tuple]:
    return list(
        (input, expected)
        for input, expected in zip(test_cases["input_value"], test_cases["expected_value"])
    )
```

and I run ruff

```shell
>>> ruff check scratch.py --select C400
>>> scratch.py:5:12: C400 Unnecessary generator (rewrite using `list()`
```

This PR fixes the error message from _"(rewrite using `list()`"_ to
_"(rewrite using `list()`)"_, and it fixes also the doc.

Related question: why I have this error message? The rule is not correct
in this case. Should I open an issue for that?
2024-03-20 14:22:34 +01:00
Charlie Marsh
f7740a8a20 Allow SPDX license headers to exceed the line length (#10481)
Closes https://github.com/astral-sh/ruff/issues/10465.
2024-03-19 15:57:03 -04:00
Dhruv Manilawala
42d4216fd7 Consider raw source code for W605 (#10480)
## Summary

This PR fixes a panic in the linter for `W605`.

Consider the following f-string:
```python
f"{{}}ab"
```

The `FStringMiddle` token would contain `{}ab`. Notice that the escaped
braces have _reduced_ the string. This means we cannot use the text
value from the token to determine the location of the escape sequence
but need to extract it from the source code.

fixes: #10434 

## Test Plan

Add new test cases and update the snapshots.
2024-03-20 00:16:35 +05:30
Charlie Marsh
bc9b4571eb Avoid failures due to non-deterministic binding ordering (#10478)
## Summary

We're seeing failures in https://github.com/astral-sh/ruff/issues/10470
because `resolve_qualified_import_name` isn't guaranteed to return a
specific import if a symbol is accessible in two ways (e.g., you have
both `import logging` and `from logging import error` in scope, and you
want `logging.error`). This PR breaks up the failing tests such that the
imports aren't in the same scope.

Closes https://github.com/astral-sh/ruff/issues/10470.

## Test Plan

I added a `bindings.reverse()` to `resolve_qualified_import_name` to
ensure that the tests pass regardless of the binding order.
2024-03-19 18:01:33 +00:00
Alex Waygood
ffd6e79677 Fix typo in string_token_flags.rs (#10476) 2024-03-19 17:43:08 +00:00
Micha Reiser
17d56ccab3 Remove unused dependencies (#10475)
## Summary
I used `cargo-shear` (see
[tweet](https://twitter.com/boshen_c/status/1770106165923586395)) to
remove some unused dependencies that `cargo udeps` wasn't reporting.

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

## Test Plan

`cargo test`
2024-03-19 17:33:47 +01:00
Auguste Lalande
363ff2a87e Clarify extend-select documentation (#10467)
<!--
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

Clarify `extend-select` documentation to avoid confusion regarding the
default `select`. Also match the `select` documentation. Resolves
#10389.

## Test Plan

<!-- How was it tested? -->
2024-03-19 09:51:31 +01:00
Charlie Marsh
938118b65c Avoid code comment detection in PEP 723 script tags (#10464)
Closes https://github.com/astral-sh/ruff/issues/10455.
2024-03-18 17:48:51 -04:00
Jane Lewis
d9f1cdbea1 ruff server sets worker thread pool size based on the user's available cores (#10399)
## Summary

Fixes #10369.

## Test Plan

N/A
2024-03-18 14:06:59 -07:00
Sid
1a2f9f082d [flake8-pytest-style] Add automatic fix for pytest-parametrize-values-wrong-type (PT007) (#10461)
## Summary

This adds automatic fixes for the `PT007` rule.

I am currently reviewing and adding Ruff rules to Home Assistant. One
rule is PT007, which has multiple hundred occurrences in the codebase,
but no automatic fix, and this is not fun to do manually, especially
because using Regexes are not really possible with this.

My knowledge of the Ruff codebase and Rust in general is not good and
this is my first PR here, so I hope it is not too bad.

One thing where I need help is: How can I have the transformed code to
be formatted automatically, instead of it being minimized as it does it
now?

## Test Plan

Using the existing fixtures and updated snapshots.
2024-03-18 20:28:49 +00:00
Alex Waygood
ae0ff9b029 Spruce up docs for flake8-pyi rules (#10422) 2024-03-18 18:03:32 +00:00
Alex Waygood
162d2eb723 Track casing of r-string prefixes in the tokenizer and AST (#10314)
Co-authored-by: Micha Reiser <micha@reiser.io>
2024-03-18 17:18:04 +00:00
Micha Reiser
31db1b6e16 Remove long Iterator::chain sequence in RuleCodePrefix::iter (#10452)
## Summary

This PR removes the `Iterator::chain(...)` sequence in
`RuleCodePrefix::iter()` with `Vec::expand` to avoid an
overlong-recursive types.

The existing `RuleCodePrefix::iter` method chains all rule group
iterators together. This leads to very long recursive types
`Chain<Map<Chain<Map<Chain<Map.....>>>>` (proportional to the number of
rule groups).

This PR rewrites the macro to use `Vec::extend` instead, which removes
the long recursive type (at the cost of introducing a potential
allocation).

## Alternatives

An alternative would be to use a stack allocated array by unrolling the
`Linter::iter` methods (generated by `EnumIter`).
I don't think it's worth the extra complexity, considering that
`RuleCodePrefix::iter` isn't a hot function.

## Test Plan

`cargo test`
2024-03-18 16:51:20 +01:00
Jane Lewis
93566f9321 ruff server default to current working directory in environments without any root directories or workspaces (#10398)
## Summary

Fixes #10324

This removes an overeager failure case where we would exit early if no
root directory or workspace folders were provided on server
initialization. We now fall-back to the current working directory as a
workspace for that file.

## Test Plan
N/A
2024-03-18 08:46:44 -07:00
trag1c
b98d8ae42e Add Nokia to "Who's Using Ruff?" (#10460)
## Summary
Added Nokia to the "Who's Using Ruff?" section in the README.

## Test Plan
Multiple teams in my department alone moved to Ruff (either exclusively
or partially) 🚀
2024-03-18 10:43:22 -05:00
Alex Waygood
92e6026446 Apply NFKC normalization to unicode identifiers in the lexer (#10412) 2024-03-18 11:56:56 +00:00
dependabot[bot]
bb540718c2 Bump the actions group with 1 update (#10445)
Bumps the actions group with 1 update:
[tj-actions/changed-files](https://github.com/tj-actions/changed-files).

Updates `tj-actions/changed-files` from 42 to 43
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tj-actions/changed-files/releases">tj-actions/changed-files's
releases</a>.</em></p>
<blockquote>
<h2>v43</h2>
<h1>Changes in v43.0.0</h1>
<h2>🔥🔥 BREAKING CHANGE 🔥🔥</h2>
<ul>
<li><code>any_{changed, modified, deleted}</code> outputs now return
<code>true</code> when no file/directory patterns are specified.</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Upgraded to v42.1.0 by <a
href="https://github.com/tj-actions-bot"><code>@​tj-actions-bot</code></a>
in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1977">tj-actions/changed-files#1977</a></li>
<li>chore(deps): lock file maintenance by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1979">tj-actions/changed-files#1979</a></li>
<li>chore(deps): update dependency
<code>@​typescript-eslint/parser</code> to v7.2.0 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1980">tj-actions/changed-files#1980</a></li>
<li>chore(deps): update dependency <code>@​types/node</code> to
v20.11.26 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1981">tj-actions/changed-files#1981</a></li>
<li>chore(deps): update dependency
<code>@​typescript-eslint/eslint-plugin</code> to v7.2.0 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1982">tj-actions/changed-files#1982</a></li>
<li>chore(deps): update dependency <code>@​types/lodash</code> to
v4.17.0 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1983">tj-actions/changed-files#1983</a></li>
<li>chore(deps): update peter-evans/create-pull-request action to v6.0.2
by <a href="https://github.com/renovate"><code>@​renovate</code></a> in
<a
href="https://redirect.github.com/tj-actions/changed-files/pull/1984">tj-actions/changed-files#1984</a></li>
<li>chore(deps): update dependency <code>@​types/node</code> to
v20.11.27 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1987">tj-actions/changed-files#1987</a></li>
<li>feat: add support for returning true for <code>any_{changed,
modified, deleted}</code> outputs when no patterns are specified by <a
href="https://github.com/jackton1"><code>@​jackton1</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1988">tj-actions/changed-files#1988</a></li>
<li>Updated README.md by <a
href="https://github.com/tj-actions-bot"><code>@​tj-actions-bot</code></a>
in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1989">tj-actions/changed-files#1989</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tj-actions/changed-files/compare/v42...v43.0.0">https://github.com/tj-actions/changed-files/compare/v42...v43.0.0</a></p>
<hr />
<h2>v43.0.0</h2>
<h2>🔥🔥 BREAKING CHANGE 🔥🔥</h2>
<ul>
<li><code>any_{changed, modified, deleted}</code> outputs now return
<code>true</code> when no file/directory patterns are specified.</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Upgraded to v42.1.0 by <a
href="https://github.com/tj-actions-bot"><code>@​tj-actions-bot</code></a>
in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1977">tj-actions/changed-files#1977</a></li>
<li>chore(deps): lock file maintenance by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1979">tj-actions/changed-files#1979</a></li>
<li>chore(deps): update dependency
<code>@​typescript-eslint/parser</code> to v7.2.0 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1980">tj-actions/changed-files#1980</a></li>
<li>chore(deps): update dependency <code>@​types/node</code> to
v20.11.26 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1981">tj-actions/changed-files#1981</a></li>
<li>chore(deps): update dependency
<code>@​typescript-eslint/eslint-plugin</code> to v7.2.0 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1982">tj-actions/changed-files#1982</a></li>
<li>chore(deps): update dependency <code>@​types/lodash</code> to
v4.17.0 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1983">tj-actions/changed-files#1983</a></li>
<li>chore(deps): update peter-evans/create-pull-request action to v6.0.2
by <a href="https://github.com/renovate"><code>@​renovate</code></a> in
<a
href="https://redirect.github.com/tj-actions/changed-files/pull/1984">tj-actions/changed-files#1984</a></li>
<li>chore(deps): update dependency <code>@​types/node</code> to
v20.11.27 by <a
href="https://github.com/renovate"><code>@​renovate</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1987">tj-actions/changed-files#1987</a></li>
<li>feat: add support for returning true for <code>any_{changed,
modified, deleted}</code> outputs when no patterns are specified by <a
href="https://github.com/jackton1"><code>@​jackton1</code></a> in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1988">tj-actions/changed-files#1988</a></li>
<li>Updated README.md by <a
href="https://github.com/tj-actions-bot"><code>@​tj-actions-bot</code></a>
in <a
href="https://redirect.github.com/tj-actions/changed-files/pull/1989">tj-actions/changed-files#1989</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/tj-actions/changed-files/compare/v42...v43.0.0">https://github.com/tj-actions/changed-files/compare/v42...v43.0.0</a></p>
<h2>v42.1.0</h2>
<p>🚀 🚀 New Feature 🚀 🚀</p>
<ul>
<li>Use changed-files output to run matrix jobs by simply setting the
new <code>matrix</code> input to <code>true</code>.</li>
</ul>
<p>This serves as an alias for setting the <code>json</code> input to
<code>true</code> and the <code>escape_json</code> input to
<code>false</code></p>
<pre lang="yml"><code>&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/tj-actions/changed-files/blob/main/HISTORY.md">tj-actions/changed-files's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v42.1.0...v43.0.0">43.0.0</a>
- (2024-03-13)</h1>
<h2><!-- raw HTML omitted -->🚀 Features</h2>
<ul>
<li>Add support for returning true for <code>any_{changed, modified,
deleted}</code> outputs when no patterns are specified (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1988">#1988</a>)
(<a
href="a5cf6aa30c">a5cf6aa</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->🔄 Update</h2>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1989">#1989</a>)</li>
</ul>
<p>Co-authored-by: repo-ranger[bot] <!-- raw HTML omitted --> (<a
href="77af4bed28">77af4be</a>)
- (tj-actions[bot])</p>
<h2><!-- raw HTML omitted -->⚙️ Miscellaneous Tasks</h2>
<ul>
<li><strong>deps:</strong> Update dependency <code>@​types/node</code>
to v20.11.27 (<a
href="15807c9c84">15807c9</a>)
- (renovate[bot])</li>
<li><strong>deps:</strong> Update peter-evans/create-pull-request action
to v6.0.2 (<a
href="dc458cf753">dc458cf</a>)
- (renovate[bot])</li>
<li><strong>deps:</strong> Update dependency <code>@​types/lodash</code>
to v4.17.0 (<a
href="92ca3eebd0">92ca3ee</a>)
- (renovate[bot])</li>
<li><strong>deps:</strong> Update dependency
<code>@​typescript-eslint/eslint-plugin</code> to v7.2.0 (<a
href="f591d0c7f0">f591d0c</a>)
- (renovate[bot])</li>
<li><strong>deps:</strong> Update dependency <code>@​types/node</code>
to v20.11.26 (<a
href="35023362e2">3502336</a>)
- (renovate[bot])</li>
<li><strong>deps:</strong> Update dependency
<code>@​typescript-eslint/parser</code> to v7.2.0 (<a
href="e436cb6d85">e436cb6</a>)
- (renovate[bot])</li>
<li><strong>deps:</strong> Lock file maintenance (<a
href="257d47dfba">257d47d</a>)
- (renovate[bot])</li>
</ul>
<h2><!-- raw HTML omitted -->⬆️ Upgrades</h2>
<ul>
<li>Upgraded to v42.1.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1977">#1977</a>)</li>
</ul>
<p>Co-authored-by: jackton1 <a
href="mailto:17484350+jackton1@users.noreply.github.com">17484350+jackton1@users.noreply.github.com</a>
(<a
href="4918e11830">4918e11</a>)
- (tj-actions[bot])</p>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v42.0.7...v42.1.0">42.1.0</a>
- (2024-03-09)</h1>
<h2><!-- raw HTML omitted -->🚀 Features</h2>
<ul>
<li>Add matrix alias to simplify using outputs for matrix jobs (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1975">#1975</a>)
(<a
href="008ba8ceec">008ba8c</a>)
- (Tonye Jack)</li>
</ul>
<h2><!-- raw HTML omitted -->🔄 Update</h2>
<ul>
<li>Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1976">#1976</a>)</li>
</ul>
<p>Co-authored-by: repo-ranger[bot] <!-- raw HTML omitted --> (<a
href="aa08304bd4">aa08304</a>)
- (tj-actions[bot])</p>
<h2><!-- raw HTML omitted -->⬆️ Upgrades</h2>
<ul>
<li>Upgraded to v42.0.7 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1974">#1974</a>)</li>
</ul>
<p>Co-authored-by: jackton1 <a
href="mailto:17484350+jackton1@users.noreply.github.com">17484350+jackton1@users.noreply.github.com</a>
(<a
href="fe6c3ea0ca">fe6c3ea</a>)
- (tj-actions[bot])</p>
<h1><a
href="https://github.com/tj-actions/changed-files/compare/v42.0.6...v42.0.7">42.0.7</a>
- (2024-03-07)</h1>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="77af4bed28"><code>77af4be</code></a>
Updated README.md (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1989">#1989</a>)</li>
<li><a
href="a5cf6aa30c"><code>a5cf6aa</code></a>
feat: add support for returning true for <code>any_{changed, modified,
deleted}</code> o...</li>
<li><a
href="15807c9c84"><code>15807c9</code></a>
chore(deps): update dependency <code>@​types/node</code> to
v20.11.27</li>
<li><a
href="dc458cf753"><code>dc458cf</code></a>
chore(deps): update peter-evans/create-pull-request action to
v6.0.2</li>
<li><a
href="92ca3eebd0"><code>92ca3ee</code></a>
chore(deps): update dependency <code>@​types/lodash</code> to
v4.17.0</li>
<li><a
href="f591d0c7f0"><code>f591d0c</code></a>
chore(deps): update dependency
<code>@​typescript-eslint/eslint-plugin</code> to v7.2.0</li>
<li><a
href="35023362e2"><code>3502336</code></a>
chore(deps): update dependency <code>@​types/node</code> to
v20.11.26</li>
<li><a
href="e436cb6d85"><code>e436cb6</code></a>
chore(deps): update dependency <code>@​typescript-eslint/parser</code>
to v7.2.0</li>
<li><a
href="257d47dfba"><code>257d47d</code></a>
chore(deps): lock file maintenance</li>
<li><a
href="4918e11830"><code>4918e11</code></a>
Upgraded to v42.1.0 (<a
href="https://redirect.github.com/tj-actions/changed-files/issues/1977">#1977</a>)</li>
<li>See full diff in <a
href="https://github.com/tj-actions/changed-files/compare/v42...v43">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tj-actions/changed-files&package-manager=github_actions&previous-version=42&new-version=43)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 09:43:02 +01:00
dependabot[bot]
916301070c Bump wasm-bindgen-test from 0.3.41 to 0.3.42 (#10448)
Bumps [wasm-bindgen-test](https://github.com/rustwasm/wasm-bindgen) from
0.3.41 to 0.3.42.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/rustwasm/wasm-bindgen/commits">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=wasm-bindgen-test&package-manager=cargo&previous-version=0.3.41&new-version=0.3.42)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-18 09:41:56 +01:00
dependabot[bot]
d6204f91cb Bump toml from 0.8.10 to 0.8.11 (#10447) 2024-03-18 08:29:03 +00:00
dependabot[bot]
ac6a9667d4 Bump proc-macro2 from 1.0.78 to 1.0.79 (#10449) 2024-03-18 08:28:42 +00:00
dependabot[bot]
c8912cf1e7 Bump thiserror from 1.0.57 to 1.0.58 (#10450) 2024-03-18 08:28:33 +00:00
dependabot[bot]
88adb9601c Bump clap from 4.5.2 to 4.5.3 (#10446) 2024-03-18 08:28:18 +00:00
Micha Reiser
12486315fb Move deviations from formatter README to documentation (#10444)
## Summary

#10151 documented the deviations between Ruff and Black with the new
2024 style guide in the `ruff-python-formatter/README.md`. However,
that's not the documentation shown
on the website when navigating to [intentional
deviations](https://docs.astral.sh/ruff/formatter/black/).

This PR streamlines the `ruff-python-formatter/README.md` and links to
the documentation on the website instead of repeating the same content.
The PR also makes the 2024 style guide deviations available on the
website documentation.

## Test Plan

I built the documentation locally and verified that the 2024 style guide
known deviations are now shown on the website.
2024-03-18 08:22:28 +00:00
Auguste Lalande
91e81413db Bug fix: Prevent fully defined links [`name`](link) from being reformatted (#10442)
## Summary

Currently fully define markdown links which include ticks are being
reformatted by

8619986123/crates/ruff_dev/src/generate_docs.rs (L105-L114)

```[`name`](link)``` -> ```[`name`][name](link)```

For example: https://docs.astral.sh/ruff/rules/typed-argument-default-in-stub/

This PR excludes the open parentheses from the regex, so that these types of links won't be reformatted.

## Test Plan

Extended the regression test.
2024-03-17 22:01:30 -04:00
Robin Caloudis
2edd61709f [flake8-quotes] Fix Autofix Error (Q000, Q002) (#10199)
## Summary
In issue https://github.com/astral-sh/ruff/issues/6785 it is reported
that a docstring in the form of `''"assert" ' SAM macro definitions '''`
is autocorrected to `"""assert" ' SAM macro definitions '''` (note the
triple quotes one only one side), which breaks the python program due
`undetermined string lateral`.

* `Q002`: Not only would docstrings in the form of `''"assert" ' SAM
macro definitions '''` (single quotes) be autofixed wrongly, but also
e.g. `""'assert' ' SAM macro definitions '''` (double quotes). The bug
is present for docstrings in all scopes (e.g. module docstrings, class
docstrings, function docstrings)

* `Q000`: The autofix error is not only present for `Q002` (docstrings),
but also for inline strings (`Q000`). Therefore `s = ''"assert" ' SAM
macro definitions '''` will also be wrongly autofixed.

Note that situation in which the first string is non-empty can be fixed,
e.g. `'123'"assert" ' SAM macro definitions '''` -> `"123""assert" ' SAM
macro definitions '''` is valid.

## What
* Change FixAvailability of `Q000` `Q002` to `Sometimes`
* Changed both rules such that docstrings/inline strings that cannot be
fixed are still reported as bad quotes via diagnostics, but no fix is
provided

## Test Plan
* For `Q000`: Add docstrings in different scopes that (partially) would
have been autofixed wrongly
* For `Q002`: Add inline strings that (partially) would have been
autofixed wrongly

Closes https://github.com/astral-sh/ruff/issues/6785
2024-03-18 01:31:25 +00:00
Auguste Lalande
dc021dd4d2 Fix pylint upstream categories not showing in docs (#10441)
## Summary

The upstream category check here

fd26b29986/crates/ruff_linter/src/upstream_categories.rs (L54-L65)

was not working because the code is actually "E0001" not "PLE0001", I
changed it so it will detect the upstream category correctly.

I also sorted the upstream categories alphabetically, so that the
document generation will be deterministic.

## Test Plan

I compared the diff before and after the change.
2024-03-18 01:27:39 +00:00
hikaru-kajita
fd26b29986 [pylint] Implement nonlocal-and-global (E115) (#10407)
## Summary

Implement `E115` in the issue #970.
Reference to pylint docs:
https://pylint.readthedocs.io/en/stable/user_guide/messages/error/nonlocal-and-global.html
Throws an error when a variable name is both declared as global and
nonlocal

## Test Plan

With `nonlocal_and_global.py`
2024-03-18 00:43:02 +00:00
Ottavio Hartman
6123a5b8bc [flake8-bugbear] Allow tuples of exceptions (B030) (#10437)
Fixes #10426 

## Summary

Fix rule B030 giving a false positive with Tuple operations like `+`.

[Playground](https://play.ruff.rs/17b086bc-cc43-40a7-b5bf-76d7d5fce78a)
```python
try:
    ...
except (ValueError,TypeError) + (EOFError,ArithmeticError):
    ...
```

## Reviewer notes

This is a little more convoluted than I was expecting -- because we can
have valid nested Tuples with operations done on them, the flattening
logic has become a bit more complex.

Shall I guard this behind --preview?

## Test Plan

Unit tested.
2024-03-18 00:31:23 +00:00
Ottavio Hartman
526abebbae [flake8-simplify] Detect implicit else cases in needless-bool (SIM103) (#10414)
Fixes #10402 

## Summary

For SIM103, detect and simplify the following case:

[playground
link](https://play.ruff.rs/d98570aa-b180-495b-8600-5c4c3fd02526)
```python
def main():
    if foo > 5:
        return True
    return False
```

## Test Plan

Unit tested only.
2024-03-18 00:15:28 +00:00
Auguste Lalande
229a50a2c8 [pylint] Implement singledispatchmethod-function (PLE5120) (#10428)
## Summary

Implement `singledispatchmethod-function` from pylint, part of #970.

This is essentially a copy paste of #8934 for `@singledispatchmethod`
decorator.

## Test Plan

Text fixture added.
2024-03-18 00:02:52 +00:00
Hoël Bagard
8619986123 Add repos/ to the gitignore (#10435)
## Summary

I would like to add `repos/` to the gitignore since it is given as an
example for the cache directory path in [the ecosystem check's
README](https://github.com/astral-sh/ruff/tree/main/python/ruff-ecosystem#development):

```console
ruff-ecosystem check ruff "./target/debug/ruff" --cache ./repos
```
2024-03-17 09:18:29 -05:00
561 changed files with 16451 additions and 9734 deletions

View File

@@ -1,21 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels: ["internal"]
groups:
actions:
patterns:
- "*"
ignore:
# The latest versions of these are not compatible with our release workflow
- dependency-name: "actions/upload-artifact"
- dependency-name: "actions/download-artifact"
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
labels: ["internal"]

60
.github/renovate.json5 vendored Normal file
View File

@@ -0,0 +1,60 @@
{
$schema: "https://docs.renovatebot.com/renovate-schema.json",
dependencyDashboard: true,
suppressNotifications: ["prEditedNotification"],
extends: ["config:recommended"],
labels: ["internal"],
schedule: ["on Monday"],
separateMajorMinor: false,
prHourlyLimit: 10,
enabledManagers: ["github-actions", "pre-commit", "cargo", "pep621", "npm"],
cargo: {
// See https://docs.renovatebot.com/configuration-options/#rangestrategy
rangeStrategy: "update-lockfile",
},
pep621: {
fileMatch: ["^(python|scripts)/.*pyproject\\.toml$"],
},
npm: {
fileMatch: ["^playground/.*package\\.json$"],
},
"pre-commit": {
enabled: true,
},
packageRules: [
{
// Group upload/download artifact updates, the versions are dependent
groupName: "Artifact GitHub Actions dependencies",
matchManagers: ["github-actions"],
matchPackagePatterns: ["actions/.*-artifact"],
description: "Weekly update of artifact-related GitHub Actions dependencies",
},
{
groupName: "pre-commit dependencies",
matchManagers: ["pre-commit"],
description: "Weekly update of pre-commit dependencies",
},
{
groupName: "NPM Development dependencies",
matchManagers: ["npm"],
matchDepTypes: ["devDependencies"],
description: "Weekly update of NPM development dependencies",
},
{
groupName: "Monaco",
matchManagers: ["npm"],
matchPackagePatterns: ["monaco"],
description: "Weekly update of the Monaco editor",
},
{
groupName: "strum",
matchManagers: ["cargo"],
matchPackagePatterns: ["strum"],
description: "Weekly update of strum dependencies",
},
],
vulnerabilityAlerts: {
commitMessageSuffix: "",
labels: ["internal", "security"],
},
}

View File

@@ -35,7 +35,7 @@ jobs:
with:
fetch-depth: 0
- uses: tj-actions/changed-files@v42
- uses: tj-actions/changed-files@v44
id: changed
with:
files_yaml: |
@@ -525,8 +525,23 @@ jobs:
- uses: Swatinem/rust-cache@v2
# Codspeed comes with a very ancient cargo version (1.66) that resolves features flags differently than what we use now.
# This can result in build failures; see https://github.com/astral-sh/ruff/pull/10700.
# There's a pending codspeed PR to upgrade to a newer cargo version, but until that's merged, we need to use the workaround below.
# https://github.com/CodSpeedHQ/codspeed-rust/pull/31
# What we do is to call cargo build manually with the correct feature flags and RUSTC settings. We'll have to
# manually maintain the list of benchmarks to run with codspeed (the benefit is that we could detect which benchmarks to run and build based on the changes).
# This is inspired by https://github.com/oxc-project/oxc/blob/a0532adc654039a0c7ead7b35216dfa0b0cb8e8f/.github/workflows/benchmark.yml
- name: "Build benchmarks"
run: cargo codspeed build --features codspeed -p ruff_benchmark
env:
RUSTFLAGS: "-C debuginfo=2 -C strip=none -g --cfg codspeed"
shell: bash
# Build all benchmarks, copy the binary to the codspeed directory, remove any `*.d` files that might have been created.
run: |
cargo build --release -p ruff_benchmark --bench parser --bench linter --bench formatter --bench lexer --features=codspeed
mkdir -p ./target/codspeed/ruff_benchmark
cp ./target/release/deps/{lexer,parser,linter,formatter}* target/codspeed/ruff_benchmark/
rm -rf ./target/codspeed/ruff_benchmark/*.d
- name: "Run benchmarks"
uses: CodSpeedHQ/action@v2

1
.gitignore vendored
View File

@@ -92,6 +92,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
repos/
# Translations
*.mo

View File

@@ -1,5 +1,90 @@
# Changelog
## 0.3.5
### Preview features
- \[`pylint`\] Implement `modified-iterating-set` (`E4703`) ([#10473](https://github.com/astral-sh/ruff/pull/10473))
- \[`refurb`\] Implement `for-loop-set-mutations` (`FURB142`) ([#10583](https://github.com/astral-sh/ruff/pull/10583))
- \[`refurb`\] Implement `unnecessary-from-float` (`FURB164`) ([#10647](https://github.com/astral-sh/ruff/pull/10647))
- \[`refurb`\] Implement `verbose-decimal-constructor` (`FURB157`) ([#10533](https://github.com/astral-sh/ruff/pull/10533))
### Rule changes
- \[`flake8-comprehensions`\] Handled special case for `C401` which also matches `C416` ([#10596](https://github.com/astral-sh/ruff/pull/10596))
- \[`flake8-pyi`\] Mark `unaliased-collections-abc-set-import` fix as "safe" for more cases in stub files (`PYI025`) ([#10547](https://github.com/astral-sh/ruff/pull/10547))
- \[`numpy`\] Add `row_stack` to NumPy 2.0 migration rule ([#10646](https://github.com/astral-sh/ruff/pull/10646))
- \[`pycodestyle`\] Allow cell magics before an import (`E402`) ([#10545](https://github.com/astral-sh/ruff/pull/10545))
- \[`pycodestyle`\] Avoid blank line rules for the first logical line in cell ([#10291](https://github.com/astral-sh/ruff/pull/10291))
### Configuration
- Respected nested namespace packages ([#10541](https://github.com/astral-sh/ruff/pull/10541))
- \[`flake8-boolean-trap`\] Add setting for user defined allowed boolean trap ([#10531](https://github.com/astral-sh/ruff/pull/10531))
### Bug fixes
- Correctly handle references in `__all__` definitions when renaming symbols in autofixes ([#10527](https://github.com/astral-sh/ruff/pull/10527))
- Track ranges of names inside `__all__` definitions ([#10525](https://github.com/astral-sh/ruff/pull/10525))
- \[`flake8-bugbear`\] Avoid false positive for usage after `continue` (`B031`) ([#10539](https://github.com/astral-sh/ruff/pull/10539))
- \[`flake8-copyright`\] Accept commas in default copyright pattern ([#9498](https://github.com/astral-sh/ruff/pull/9498))
- \[`flake8-datetimez`\] Allow f-strings with `%z` for `DTZ007` ([#10651](https://github.com/astral-sh/ruff/pull/10651))
- \[`flake8-pytest-style`\] Fix `PT014` autofix for last item in list ([#10532](https://github.com/astral-sh/ruff/pull/10532))
- \[`flake8-quotes`\] Ignore `Q000`, `Q001` when string is inside forward ref ([#10585](https://github.com/astral-sh/ruff/pull/10585))
- \[`isort`\] Always place non-relative imports after relative imports ([#10669](https://github.com/astral-sh/ruff/pull/10669))
- \[`isort`\] Respect Unicode characters in import sorting ([#10529](https://github.com/astral-sh/ruff/pull/10529))
- \[`pyflakes`\] Fix F821 false negatives when `from __future__ import annotations` is active (attempt 2) ([#10524](https://github.com/astral-sh/ruff/pull/10524))
- \[`pyflakes`\] Make `unnecessary-lambda` an always-unsafe fix ([#10668](https://github.com/astral-sh/ruff/pull/10668))
- \[`pylint`\] Fixed false-positive on the rule `PLW1641` (`eq-without-hash`) ([#10566](https://github.com/astral-sh/ruff/pull/10566))
- \[`ruff`\] Fix panic in unused `# noqa` removal with multi-byte space (`RUF100`) ([#10682](https://github.com/astral-sh/ruff/pull/10682))
### Documentation
- Add PR title format to `CONTRIBUTING.md` ([#10665](https://github.com/astral-sh/ruff/pull/10665))
- Fix list markup to include blank lines required ([#10591](https://github.com/astral-sh/ruff/pull/10591))
- Put `flake8-logging` next to the other flake8 plugins in registry ([#10587](https://github.com/astral-sh/ruff/pull/10587))
- \[`flake8-bandit`\] Update warning message for rule `S305` to address insecure block cipher mode use ([#10602](https://github.com/astral-sh/ruff/pull/10602))
- \[`flake8-bugbear`\] Document use of anonymous assignment in `useless-expression` ([#10551](https://github.com/astral-sh/ruff/pull/10551))
- \[`flake8-datetimez`\] Clarify error messages and docs for `DTZ` rules ([#10621](https://github.com/astral-sh/ruff/pull/10621))
- \[`pycodestyle`\] Use same before vs. after numbers for `space-around-operator` ([#10640](https://github.com/astral-sh/ruff/pull/10640))
- \[`ruff`\] Change `quadratic-list-summation` docs to use `iadd` consistently ([#10666](https://github.com/astral-sh/ruff/pull/10666))
## 0.3.4
### Preview features
- \[`flake8-simplify`\] Detect implicit `else` cases in `needless-bool` (`SIM103`) ([#10414](https://github.com/astral-sh/ruff/pull/10414))
- \[`pylint`\] Implement `nan-comparison` (`PLW0117`) ([#10401](https://github.com/astral-sh/ruff/pull/10401))
- \[`pylint`\] Implement `nonlocal-and-global` (`E115`) ([#10407](https://github.com/astral-sh/ruff/pull/10407))
- \[`pylint`\] Implement `singledispatchmethod-function` (`PLE5120`) ([#10428](https://github.com/astral-sh/ruff/pull/10428))
- \[`refurb`\] Implement `list-reverse-copy` (`FURB187`) ([#10212](https://github.com/astral-sh/ruff/pull/10212))
### Rule changes
- \[`flake8-pytest-style`\] Add automatic fix for `pytest-parametrize-values-wrong-type` (`PT007`) ([#10461](https://github.com/astral-sh/ruff/pull/10461))
- \[`pycodestyle`\] Allow SPDX license headers to exceed the line length (`E501`) ([#10481](https://github.com/astral-sh/ruff/pull/10481))
### Formatter
- Fix unstable formatting for trailing subscript end-of-line comment ([#10492](https://github.com/astral-sh/ruff/pull/10492))
### Bug fixes
- Avoid code comment detection in PEP 723 script tags ([#10464](https://github.com/astral-sh/ruff/pull/10464))
- Avoid incorrect tuple transformation in single-element case (`C409`) ([#10491](https://github.com/astral-sh/ruff/pull/10491))
- Bug fix: Prevent fully defined links [`name`](link) from being reformatted ([#10442](https://github.com/astral-sh/ruff/pull/10442))
- Consider raw source code for `W605` ([#10480](https://github.com/astral-sh/ruff/pull/10480))
- Docs: Link inline settings when not part of options section ([#10499](https://github.com/astral-sh/ruff/pull/10499))
- Don't treat annotations as redefinitions in `.pyi` files ([#10512](https://github.com/astral-sh/ruff/pull/10512))
- Fix `E231` bug: Inconsistent catch compared to pycodestyle, such as when dict nested in list ([#10469](https://github.com/astral-sh/ruff/pull/10469))
- Fix pylint upstream categories not showing in docs ([#10441](https://github.com/astral-sh/ruff/pull/10441))
- Add missing `Options` references to blank line docs ([#10498](https://github.com/astral-sh/ruff/pull/10498))
- 'Revert "F821: Fix false negatives in .py files when `from __future__ import annotations` is active (#10362)"' ([#10513](https://github.com/astral-sh/ruff/pull/10513))
- Apply NFKC normalization to unicode identifiers in the lexer ([#10412](https://github.com/astral-sh/ruff/pull/10412))
- Avoid failures due to non-deterministic binding ordering ([#10478](https://github.com/astral-sh/ruff/pull/10478))
- \[`flake8-bugbear`\] Allow tuples of exceptions (`B030`) ([#10437](https://github.com/astral-sh/ruff/pull/10437))
- \[`flake8-quotes`\] Avoid syntax errors due to invalid quotes (`Q000, Q002`) ([#10199](https://github.com/astral-sh/ruff/pull/10199))
## 0.3.3
### Preview features
@@ -61,7 +146,7 @@
- Fix unstable `with` items formatting ([#10274](https://github.com/astral-sh/ruff/pull/10274))
- Avoid repeating function calls in f-string conversions ([#10265](https://github.com/astral-sh/ruff/pull/10265))
- Fix E203 false positive for slices in format strings ([#10280](https://github.com/astral-sh/ruff/pull/10280))
- Fix incorrect `Parameter` range for `*args` and `**kwargs` ([#10283](https://github.com/astral-sh/ruff/pull/10283))
- Fix incorrect `Parameter` range for `*args` and `**kwargs` ([#10283](https://github.com/astral-sh/ruff/pull/10283))
- Treat `typing.Annotated` subscripts as type definitions ([#10285](https://github.com/astral-sh/ruff/pull/10285))
## 0.3.1
@@ -169,8 +254,7 @@ This release introduces the Ruff 2024.2 style, stabilizing the following changes
Highlights include:
- Initial support formatting f-strings (in `--preview`).
- Support for overriding arbitrary configuration options via the CLI through an expanded `--config`
argument (e.g., `--config "lint.isort.combine-as-imports=false"`).
- Support for overriding arbitrary configuration options via the CLI through an expanded `--config` argument (e.g., `--config "lint.isort.combine-as-imports=false"`).
- Significant performance improvements in Ruff's lexer, parser, and lint rules.
### Preview features
@@ -818,7 +902,7 @@ docstrings via the `docstring-code-format` setting.
- \[`pylint`\] Default `max-positional-args` to `max-args` ([#8998](https://github.com/astral-sh/ruff/pull/8998))
- \[`pylint`\] Add `allow-dunder-method-names` setting for `bad-dunder-method-name` (`PLW3201`) ([#8812](https://github.com/astral-sh/ruff/pull/8812))
- \[`isort`\] Add support for `from-first` setting ([#8663](https://github.com/astral-sh/ruff/pull/8663))
- \[`isort`\] Add support for `length-sort` settings ([#8841](https://github.com/astral-sh/ruff/pull/8841))
- \[`isort`\] Add support for `length-sort` settings ([#8841](https://github.com/astral-sh/ruff/pull/8841))
### Bug fixes
@@ -947,7 +1031,7 @@ docstrings via the `docstring-code-format` setting.
- \[`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))
- Make `SIM118` fix as safe when the expression is a known dictionary ([#8525](https://github.com/astral-sh/ruff/pull/8525))
### Formatter
@@ -1115,7 +1199,7 @@ Try it today with `ruff format`! [Check out the blog post](https://astral.sh/blo
- Add `backports.strenum` to `deprecated-imports` ([#8113](https://github.com/astral-sh/ruff/pull/8113))
- Update `SIM112` to ignore `https_proxy`, `http_proxy`, and `no_proxy` ([#8140](https://github.com/astral-sh/ruff/pull/8140))
- Update fix for `literal-membership` (`PLR6201`) to be unsafe ([#8097](https://github.com/astral-sh/ruff/pull/8097))
- Update fix for `mutable-argument-defaults` (`B006`) to be unsafe ([#8108](https://github.com/astral-sh/ruff/pull/8108))
- Update fix for `mutable-argument-defaults` (`B006`) to be unsafe ([#8108](https://github.com/astral-sh/ruff/pull/8108))
### Formatter
@@ -1243,7 +1327,7 @@ Read Ruff's new [versioning policy](https://docs.astral.sh/ruff/versioning/).
- \[`refurb`\] Add `single-item-membership-test` (`FURB171`) ([#7815](https://github.com/astral-sh/ruff/pull/7815))
- \[`pylint`\] Add `and-or-ternary` (`R1706`) ([#7811](https://github.com/astral-sh/ruff/pull/7811))
*New rules are added in [preview](https://docs.astral.sh/ruff/preview/).*
_New rules are added in [preview](https://docs.astral.sh/ruff/preview/)._
### Configuration

View File

@@ -33,27 +33,18 @@ Welcome! We're happy to have you here. Thank you in advance for your contributio
## The Basics
Ruff welcomes contributions in the form of Pull Requests.
Ruff welcomes contributions in the form of pull requests.
For small changes (e.g., bug fixes), feel free to submit a PR.
For larger changes (e.g., new lint rules, new functionality, new configuration options), consider
creating an [**issue**](https://github.com/astral-sh/ruff/issues) outlining your proposed change.
You can also join us on [**Discord**](https://discord.com/invite/astral-sh) to discuss your idea with the
You can also join us on [Discord](https://discord.com/invite/astral-sh) to discuss your idea with the
community. We've labeled [beginner-friendly tasks](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
in the issue tracker, along with [bugs](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
and [improvements](https://github.com/astral-sh/ruff/issues?q=is%3Aissue+is%3Aopen+label%3Aaccepted)
that are ready for contributions.
If you're looking for a place to start, we recommend implementing a new lint rule (see:
[_Adding a new lint rule_](#example-adding-a-new-lint-rule), which will allow you to learn from and
pattern-match against the examples in the existing codebase. Many lint rules are inspired by
existing Python plugins, which can be used as a reference implementation.
As a concrete example: consider taking on one of the rules from the [`flake8-pyi`](https://github.com/astral-sh/ruff/issues/848)
plugin, and looking to the originating [Python source](https://github.com/PyCQA/flake8-pyi) for
guidance.
If you have suggestions on how we might improve the contributing documentation, [let us know](https://github.com/astral-sh/ruff/discussions/5693)!
### Prerequisites
@@ -107,7 +98,7 @@ 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.
```
These checks will run on GitHub Actions when you open your Pull Request, but running them locally
These checks will run on GitHub Actions when you open your pull request, but running them locally
will save you time and expedite the merge process.
Note that many code changes also require updating the snapshot tests, which is done interactively
@@ -117,7 +108,14 @@ after running `cargo test` like so:
cargo insta review
```
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
If your pull request relates to a specific lint rule, include the category and rule code in the
title, as in the following examples:
- \[`flake8-bugbear`\] Avoid false positive for usage after `continue` (`B031`)
- \[`flake8-simplify`\] Detect implicit `else` cases in `needless-bool` (`SIM103`)
- \[`pycodestyle`\] Implement `redundant-backslash` (`E502`)
Your pull request will be reviewed by a maintainer, which may involve a few rounds of iteration
prior to merging.
### Project Structure
@@ -125,8 +123,8 @@ prior to merging.
Ruff is structured as a monorepo with a [flat crate structure](https://matklad.github.io/2021/08/22/large-rust-workspaces.html),
such that all crates are contained in a flat `crates` directory.
The vast majority of the code, including all lint rules, lives in the `ruff` crate (located at
`crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
The vast majority of the code, including all lint rules, lives in the `ruff_linter` crate (located
at `crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
At the time of writing, the repository includes the following crates:
@@ -199,11 +197,14 @@ and calling out to lint rule analyzer functions as it goes.
If you need to inspect the AST, you can run `cargo dev print-ast` with a Python file. Grep
for the `Diagnostic::new` invocations to understand how other, similar rules are implemented.
Once you're satisfied with your code, add tests for your rule. See [rule testing](#rule-testing-fixtures-and-snapshots)
for more details.
Once you're satisfied with your code, add tests for your rule
(see: [rule testing](#rule-testing-fixtures-and-snapshots)), and regenerate the documentation and
associated assets (like our JSON Schema) with `cargo dev generate-all`.
Finally, regenerate the documentation and other generated assets (like our JSON Schema) with:
`cargo dev generate-all`.
Finally, submit a pull request, and include the category, rule name, and rule code in the title, as
in:
> \[`pycodestyle`\] Implement `redundant-backslash` (`E502`)
#### Rule naming convention
@@ -813,8 +814,8 @@ To understand Ruff's import categorization system, we first need to define two c
"project root".)
- "Package root": The top-most directory defining the Python package that includes a given Python
file. To find the package root for a given Python file, traverse up its parent directories until
you reach a parent directory that doesn't contain an `__init__.py` file (and isn't marked as
a [namespace package](https://docs.astral.sh/ruff/settings/#namespace-packages)); take the directory
you reach a parent directory that doesn't contain an `__init__.py` file (and isn't in a subtree
marked as a [namespace package](https://docs.astral.sh/ruff/settings/#namespace-packages)); take the directory
just before that, i.e., the first directory in the package.
For example, given:

407
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,22 +12,20 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
license = "MIT"
[workspace.dependencies]
aho-corasick = { version = "1.1.2" }
aho-corasick = { version = "1.1.3" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
anstream = { version = "0.6.13" }
anyhow = { version = "1.0.80" }
argfile = { version = "0.1.6" }
assert_cmd = { version = "2.0.13" }
bincode = { version = "1.3.3" }
bitflags = { version = "2.4.1" }
bitflags = { version = "2.5.0" }
bstr = { version = "1.9.1" }
cachedir = { version = "0.3.1" }
chrono = { version = "0.4.35", default-features = false, features = ["clock"] }
clap = { version = "4.5.2", features = ["derive"] }
clap = { version = "4.5.3", features = ["derive"] }
clap_complete_command = { version = "0.5.1" }
clearscreen = { version = "2.0.0" }
codspeed-criterion-compat = { version = "2.4.0", default-features = false }
colored = { version = "2.1.0" }
configparser = { version = "3.0.3" }
console_error_panic_hook = { version = "0.1.7" }
console_log = { version = "1.0.0" }
countme = { version = "3.0.1" }
@@ -35,7 +33,7 @@ criterion = { version = "0.5.1", default-features = false }
crossbeam = { version = "0.8.4" }
dirs = { version = "5.0.0" }
drop_bomb = { version = "0.1.5" }
env_logger = { version = "0.10.1" }
env_logger = { version = "0.11.0" }
fern = { version = "0.6.1" }
filetime = { version = "0.2.23" }
fs-err = { version = "2.11.0" }
@@ -48,7 +46,7 @@ imperative = { version = "1.0.4" }
indicatif = { version = "0.17.8" }
indoc = { version = "2.0.4" }
insta = { version = "1.35.1", feature = ["filters", "glob"] }
insta-cmd = { version = "0.4.0" }
insta-cmd = { version = "0.5.0" }
is-macro = { version = "0.3.5" }
is-wsl = { version = "0.4.0" }
itertools = { version = "0.12.1" }
@@ -65,17 +63,19 @@ memchr = { version = "2.7.1" }
mimalloc = { version = "0.1.39" }
natord = { version = "1.0.9" }
notify = { version = "6.1.1" }
num_cpus = { version = "1.16.0" }
once_cell = { version = "1.19.0" }
owo-colors = { version = "4.0.0" }
path-absolutize = { version = "3.1.1" }
pathdiff = { version = "0.2.1" }
pep440_rs = { version = "0.4.0", features = ["serde"] }
pep440_rs = { version = "0.5.0", features = ["serde"] }
pretty_assertions = "1.3.0"
proc-macro2 = { version = "1.0.78" }
proc-macro2 = { version = "1.0.79" }
pyproject-toml = { version = "0.9.0" }
quick-junit = { version = "0.3.5" }
quote = { version = "1.0.23" }
rand = { version = "0.8.5" }
rayon = { version = "1.8.1" }
rayon = { version = "1.10.0" }
regex = { version = "1.10.2" }
result-like = { version = "0.5.0" }
rustc-hash = { version = "1.1.0" }
@@ -89,31 +89,32 @@ serde_with = { version = "3.6.0", default-features = false, features = ["macros"
shellexpand = { version = "3.0.0" }
shlex = { version = "1.3.0" }
similar = { version = "2.4.0", features = ["inline"] }
smallvec = { version = "1.13.1" }
smallvec = { version = "1.13.2" }
static_assertions = "1.1.0"
strum = { version = "0.25.0", features = ["strum_macros"] }
strum_macros = { version = "0.25.3" }
syn = { version = "2.0.51" }
strum = { version = "0.26.0", features = ["strum_macros"] }
strum_macros = { version = "0.26.0" }
syn = { version = "2.0.55" }
tempfile = { version = "3.9.0" }
test-case = { version = "3.3.1" }
thiserror = { version = "1.0.57" }
thiserror = { version = "1.0.58" }
tikv-jemallocator = { version = "0.5.0" }
toml = { version = "0.8.9" }
toml = { version = "0.8.11" }
tracing = { version = "0.1.40" }
tracing-indicatif = { version = "0.3.6" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-tree = { version = "0.2.4" }
tracing-tree = { version = "0.3.0" }
typed-arena = { version = "2.0.2" }
unic-ucd-category = { version = "0.9" }
unicode-ident = { version = "1.0.12" }
unicode-width = { version = "0.1.11" }
unicode_names2 = { version = "1.2.2" }
unicode-normalization = { version = "0.1.23" }
ureq = { version = "2.9.6" }
url = { version = "2.5.0" }
uuid = { version = "1.6.1", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
walkdir = { version = "2.3.2" }
wasm-bindgen = { version = "0.2.92" }
wasm-bindgen-test = { version = "0.3.40" }
wasm-bindgen-test = { version = "0.3.42" }
wild = { version = "2" }
[workspace.lints.rust]

View File

@@ -32,7 +32,7 @@ An extremely fast Python linter and code formatter, written in Rust.
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruff-compare-to-flake8), isort, and Black
- 📦 Built-in caching, to avoid re-analyzing unchanged files
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
- 📏 Over [700 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
- 📏 Over [800 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
of popular Flake8 plugins, like flake8-bugbear
- ⌨️ First-party [editor integrations](https://docs.astral.sh/ruff/integrations/) for
[VS Code](https://github.com/astral-sh/ruff-vscode) and [more](https://github.com/astral-sh/ruff-lsp)
@@ -151,7 +151,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
```yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.3.3
rev: v0.3.5
hooks:
# Run the linter.
- id: ruff
@@ -272,7 +272,7 @@ for more on the linting and formatting commands, respectively.
<!-- Begin section: Rules -->
**Ruff supports over 700 lint rules**, many of which are inspired by popular tools like Flake8,
**Ruff supports over 800 lint rules**, many of which are inspired by popular tools like Flake8,
isort, pyupgrade, and others. Regardless of the rule's origin, Ruff re-implements every rule in
Rust as a first-party feature.
@@ -429,6 +429,7 @@ Ruff is used by a number of major open-source projects and companies, including:
- [Mypy](https://github.com/python/mypy)
- Netflix ([Dispatch](https://github.com/Netflix/dispatch))
- [Neon](https://github.com/neondatabase/neon)
- [Nokia](https://nokia.com/)
- [NoneBot](https://github.com/nonebot/nonebot2)
- [NumPyro](https://github.com/pyro-ppl/numpyro)
- [ONNX](https://github.com/onnx/onnx)

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff"
version = "0.3.3"
version = "0.3.5"
publish = false
authors = { workspace = true }
edition = { workspace = true }
@@ -25,6 +25,7 @@ ruff_source_file = { path = "../ruff_source_file" }
ruff_text_size = { path = "../ruff_text_size" }
ruff_workspace = { path = "../ruff_workspace" }
anstream = { workspace = true }
anyhow = { workspace = true }
argfile = { workspace = true }
bincode = { workspace = true }
@@ -34,13 +35,14 @@ chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env", "wrap_help"] }
clap_complete_command = { workspace = true }
clearscreen = { workspace = true }
colored = { workspace = true }
filetime = { workspace = true }
ignore = { workspace = true }
is-macro = { workspace = true }
itertools = { workspace = true }
log = { workspace = true }
notify = { workspace = true }
num_cpus = { workspace = true }
owo-colors = { workspace = true }
path-absolutize = { workspace = true, features = ["once_cell_cache"] }
rayon = { workspace = true }
regex = { workspace = true }
@@ -53,7 +55,7 @@ tempfile = { workspace = true }
thiserror = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true, features = ["log"] }
tracing-subscriber = { workspace = true, features = ["registry"]}
tracing-subscriber = { workspace = true, features = ["registry"] }
tracing-tree = { workspace = true }
walkdir = { workspace = true }
wild = { workspace = true }
@@ -61,9 +63,6 @@ wild = { workspace = true }
[dev-dependencies]
# Enable test rules during development
ruff_linter = { path = "../ruff_linter", features = ["clap", "test-rules"] }
assert_cmd = { workspace = true }
# Avoid writing colored snapshots when running tests from the terminal
colored = { workspace = true, features = ["no-color"]}
insta = { workspace = true, features = ["filters", "json"] }
insta-cmd = { workspace = true }
tempfile = { workspace = true }

View File

@@ -8,7 +8,7 @@ use std::sync::Arc;
use anyhow::bail;
use clap::builder::{TypedValueParser, ValueParserFactory};
use clap::{command, Parser};
use colored::Colorize;
use owo_colors::OwoColorize;
use path_absolutize::path_dedot;
use regex::Regex;
use rustc_hash::FxHashMap;
@@ -496,7 +496,7 @@ pub struct FormatCommand {
pub range: Option<FormatRange>,
}
#[derive(Clone, Debug, clap::Parser)]
#[derive(Copy, Clone, Debug, clap::Parser)]
pub struct ServerCommand {
/// Enable preview mode; required for regular operation
#[arg(long)]
@@ -1053,7 +1053,6 @@ pub enum FormatRangeParseError {
impl std::fmt::Display for FormatRangeParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let tip = " tip:".bold().green();
match self {
FormatRangeParseError::StartGreaterThanEnd(start, end) => {
write!(
@@ -1062,7 +1061,8 @@ impl std::fmt::Display for FormatRangeParseError {
start_invalid=start.to_string().bold().yellow(),
end_invalid=end.to_string().bold().yellow(),
start=start.to_string().green().bold(),
end=end.to_string().green().bold()
end=end.to_string().green().bold(),
tip=" tip:".bold().green()
)
}
FormatRangeParseError::InvalidStart(inner) => inner.write(f, true),
@@ -1148,7 +1148,8 @@ pub enum LineColumnParseError {
impl LineColumnParseError {
fn write(&self, f: &mut std::fmt::Formatter, start_range: bool) -> std::fmt::Result {
let tip = "tip:".bold().green();
let tip = "tip:".bold();
let tip = tip.green();
let range = if start_range { "start" } else { "end" };

View File

@@ -4,9 +4,9 @@ use std::path::{Path, PathBuf};
use std::time::Instant;
use anyhow::Result;
use colored::Colorize;
use ignore::Error;
use log::{debug, error, warn};
use owo_colors::OwoColorize;
#[cfg(not(target_family = "wasm"))]
use rayon::prelude::*;
use rustc_hash::FxHashMap;
@@ -226,6 +226,7 @@ mod test {
use rustc_hash::FxHashMap;
use tempfile::TempDir;
use ruff_linter::colors;
use ruff_linter::message::{Emitter, EmitterContext, TextEmitter};
use ruff_linter::registry::Rule;
use ruff_linter::settings::types::UnsafeFixes;
@@ -252,6 +253,7 @@ mod test {
for file in [&pyproject_toml, &python_file, &notebook] {
fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.mode(0o000)
.open(file)?;
@@ -279,7 +281,7 @@ mod test {
UnsafeFixes::Enabled,
)
.unwrap();
let mut output = Vec::new();
let mut output = colors::none(Vec::new());
TextEmitter::default()
.with_show_fix_status(true)
@@ -290,7 +292,7 @@ mod test {
)
.unwrap();
let messages = String::from_utf8(output).unwrap();
let messages = String::from_utf8(output.into_inner()).unwrap();
insta::with_settings!({
omit_expression => true,

View File

@@ -2,7 +2,7 @@ use std::fs::remove_dir_all;
use std::io::{self, BufWriter, Write};
use anyhow::Result;
use colored::Colorize;
use owo_colors::OwoColorize;
use path_absolutize::path_dedot;
use walkdir::WalkDir;

View File

@@ -6,9 +6,9 @@ use std::path::{Path, PathBuf};
use std::time::Instant;
use anyhow::Result;
use colored::Colorize;
use itertools::Itertools;
use log::{error, warn};
use owo_colors::OwoColorize;
use rayon::iter::Either::{Left, Right};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use rustc_hash::FxHashSet;
@@ -16,6 +16,7 @@ use thiserror::Error;
use tracing::debug;
use ruff_diagnostics::SourceMap;
use ruff_linter::colors;
use ruff_linter::fs;
use ruff_linter::logging::{DisplayParseError, LogLevel};
use ruff_linter::registry::Rule;
@@ -189,10 +190,10 @@ pub(crate) fn format(
match mode {
FormatMode::Write => {}
FormatMode::Check => {
results.write_changed(&mut stdout().lock())?;
results.write_changed(&mut colors::auto(stdout()).lock())?;
}
FormatMode::Diff => {
results.write_diff(&mut stdout().lock())?;
results.write_diff(&mut colors::auto(stdout()).lock())?;
}
}
@@ -200,9 +201,9 @@ pub(crate) fn format(
if config_arguments.log_level >= LogLevel::Default {
if mode.is_diff() {
// Allow piping the diff to e.g. a file by writing the summary to stderr
results.write_summary(&mut stderr().lock())?;
results.write_summary(&mut colors::auto(stderr()).lock())?;
} else {
results.write_summary(&mut stdout().lock())?;
results.write_summary(&mut colors::auto(stdout()).lock())?;
}
}

View File

@@ -4,6 +4,7 @@ use std::path::Path;
use anyhow::Result;
use log::error;
use ruff_linter::colors;
use ruff_linter::source_kind::SourceKind;
use ruff_python_ast::{PySourceType, SourceType};
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, Resolver};
@@ -111,7 +112,7 @@ fn format_source_code(
match &formatted {
FormattedSource::Formatted(formatted) => match mode {
FormatMode::Write => {
let mut writer = stdout().lock();
let mut writer = colors::auto(stdout()).lock();
formatted
.write(&mut writer)
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;
@@ -120,7 +121,7 @@ fn format_source_code(
FormatMode::Diff => {
use std::io::Write;
write!(
&mut stdout().lock(),
&mut colors::auto(stdout()).lock(),
"{}",
source_kind.diff(formatted, path).unwrap()
)
@@ -130,7 +131,7 @@ fn format_source_code(
FormattedSource::Unchanged => {
// Write to stdout regardless of whether the source was formatted
if mode.is_write() {
let mut writer = stdout().lock();
let mut writer = colors::auto(stdout()).lock();
source_kind
.write(&mut writer)
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;

View File

@@ -1,3 +1,5 @@
use std::num::NonZeroUsize;
use crate::ExitStatus;
use anyhow::Result;
use ruff_linter::logging::LogLevel;
@@ -9,7 +11,11 @@ use tracing_subscriber::{
};
use tracing_tree::time::Uptime;
pub(crate) fn run_server(preview: bool, log_level: LogLevel) -> Result<ExitStatus> {
pub(crate) fn run_server(
preview: bool,
worker_threads: NonZeroUsize,
log_level: LogLevel,
) -> Result<ExitStatus> {
if !preview {
tracing::error!("--preview needs to be provided as a command line argument while the server is still unstable.\nFor example: `ruff server --preview`");
return Ok(ExitStatus::Error);
@@ -33,7 +39,7 @@ pub(crate) fn run_server(preview: bool, log_level: LogLevel) -> Result<ExitStatu
tracing::subscriber::set_global_default(subscriber)?;
let server = Server::new()?;
let server = Server::new(worker_threads)?;
server.run().map(|()| ExitStatus::Success)
}

View File

@@ -8,11 +8,12 @@ use std::ops::{Add, AddAssign};
use std::path::Path;
use anyhow::{Context, Result};
use colored::Colorize;
use log::{debug, error, warn};
use owo_colors::OwoColorize;
use rustc_hash::FxHashMap;
use ruff_diagnostics::Diagnostic;
use ruff_linter::colors;
use ruff_linter::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult, ParseSource};
use ruff_linter::logging::DisplayParseError;
use ruff_linter::message::Message;
@@ -444,7 +445,7 @@ pub(crate) fn lint_stdin(
// But only write a diff if it's non-empty.
if !fixed.is_empty() {
write!(
&mut io::stdout().lock(),
&mut colors::auto(io::stdout()).lock(),
"{}",
source_kind.diff(&transformed, path).unwrap()
)?;

View File

@@ -2,6 +2,7 @@
use std::fs::File;
use std::io::{self, stdout, BufWriter, Write};
use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
use std::process::ExitCode;
use std::sync::mpsc::channel;
@@ -9,10 +10,11 @@ use std::sync::mpsc::channel;
use anyhow::Result;
use args::{GlobalConfigArgs, ServerCommand};
use clap::CommandFactory;
use colored::Colorize;
use log::warn;
use notify::{recommended_watcher, RecursiveMode, Watcher};
use owo_colors::OwoColorize;
use ruff_linter::colors;
use ruff_linter::logging::{set_up_logging, LogLevel};
use ruff_linter::settings::flags::FixMode;
use ruff_linter::settings::types::SerializationFormat;
@@ -144,10 +146,6 @@ pub fn run(
}));
}
// Enabled ANSI colors on Windows 10.
#[cfg(windows)]
assert!(colored::control::set_virtual_terminal(true).is_ok());
set_up_logging(global_options.log_level())?;
if let Some(deprecated_alias_warning) = deprecated_alias_warning {
@@ -204,10 +202,15 @@ fn format(args: FormatCommand, global_options: GlobalConfigArgs) -> Result<ExitS
}
}
#[allow(clippy::needless_pass_by_value)] // TODO: remove once we start taking arguments from here
fn server(args: ServerCommand, log_level: LogLevel) -> Result<ExitStatus> {
let ServerCommand { preview } = args;
commands::server::run_server(preview, log_level)
// by default, we set the number of worker threads to `num_cpus`, with a maximum of 4.
let worker_threads = num_cpus::get().max(4);
commands::server::run_server(
preview,
NonZeroUsize::try_from(worker_threads).expect("a non-zero worker thread count"),
log_level,
)
}
pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<ExitStatus> {
@@ -219,13 +222,12 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
let mut writer: Box<dyn Write> = match cli.output_file {
Some(path) if !cli.watch => {
colored::control::set_override(false);
let file = File::create(path)?;
Box::new(BufWriter::new(file))
Box::new(BufWriter::new(colors::none(file)))
}
_ => Box::new(BufWriter::new(io::stdout())),
_ => Box::new(BufWriter::new(colors::auto(io::stdout()))),
};
let stderr_writer = Box::new(BufWriter::new(io::stderr()));
let stderr_writer = Box::new(BufWriter::new(colors::auto(io::stderr())));
let is_stdin = is_stdin(&cli.files, cli.stdin_filename.as_deref());
let files = resolve_default_files(cli.files, is_stdin);

View File

@@ -1,7 +1,8 @@
use std::process::ExitCode;
use anstream::eprintln;
use clap::{Parser, Subcommand};
use colored::Colorize;
use owo_colors::OwoColorize;
use ruff::args::{Args, Command};
use ruff::{run, ExitStatus};

View File

@@ -16,7 +16,7 @@ impl std::fmt::Display for PanicError {
}
thread_local! {
static LAST_PANIC: std::cell::Cell<Option<PanicError>> = std::cell::Cell::new(None);
static LAST_PANIC: std::cell::Cell<Option<PanicError>> = const { std::cell::Cell::new(None) };
}
/// [`catch_unwind`](std::panic::catch_unwind) wrapper that sets a custom [`set_hook`](std::panic::set_hook)

View File

@@ -5,8 +5,8 @@ use std::io::Write;
use anyhow::Result;
use bitflags::bitflags;
use colored::Colorize;
use itertools::{iterate, Itertools};
use owo_colors::OwoColorize;
use serde::Serialize;
use ruff_linter::fs::relativize_path;

View File

@@ -70,7 +70,7 @@ pub(crate) fn version() -> VersionInfo {
#[cfg(test)]
mod tests {
use insta::{assert_display_snapshot, assert_json_snapshot};
use insta::{assert_json_snapshot, assert_snapshot};
use super::{CommitInfo, VersionInfo};
@@ -80,7 +80,7 @@ mod tests {
version: "0.0.0".to_string(),
commit_info: None,
};
assert_display_snapshot!(version);
assert_snapshot!(version);
}
#[test]
@@ -95,7 +95,7 @@ mod tests {
commits_since_last_tag: 0,
}),
};
assert_display_snapshot!(version);
assert_snapshot!(version);
}
#[test]
@@ -110,7 +110,7 @@ mod tests {
commits_since_last_tag: 24,
}),
};
assert_display_snapshot!(version);
assert_snapshot!(version);
}
#[test]

View File

@@ -1353,6 +1353,7 @@ fn unreadable_pyproject_toml() -> Result<()> {
// Create an empty file with 000 permissions
fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.mode(0o000)
.open(pyproject_toml)?;

View File

@@ -231,7 +231,7 @@ linter.flake8_bandit.check_typed_exception = false
linter.flake8_bugbear.extend_immutable_calls = []
linter.flake8_builtins.builtins_ignorelist = []
linter.flake8_comprehensions.allow_dict_calls_with_keyword_arguments = false
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}(-\d{4})*
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}((-|,\s)\d{4})*
linter.flake8_copyright.author = none
linter.flake8_copyright.min_file_size = 0
linter.flake8_errmsg.max_string_length = 0

View File

@@ -22,7 +22,7 @@ ruff_python_formatter = { path = "../ruff_python_formatter" }
ruff_python_parser = { path = "../ruff_python_parser" }
ruff_python_stdlib = { path = "../ruff_python_stdlib" }
ruff_python_trivia = { path = "../ruff_python_trivia" }
ruff_workspace = { path = "../ruff_workspace", features = ["schemars"]}
ruff_workspace = { path = "../ruff_workspace", features = ["schemars"] }
anyhow = { workspace = true }
clap = { workspace = true, features = ["wrap_help"] }
@@ -31,7 +31,6 @@ imara-diff = { workspace = true }
indicatif = { workspace = true }
itertools = { workspace = true }
libcst = { workspace = true }
once_cell = { workspace = true }
pretty_assertions = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }

View File

@@ -134,7 +134,7 @@ impl Statistics {
}
}
/// We currently prefer the the similarity index, but i'd like to keep this around
/// We currently prefer the similarity index, but i'd like to keep this around
#[allow(clippy::cast_precision_loss, unused)]
pub(crate) fn jaccard_index(&self) -> f32 {
self.intersection as f32 / (self.black_input + self.ruff_output + self.intersection) as f32

View File

@@ -1,6 +1,7 @@
//! Generate Markdown documentation for applicable rules.
#![allow(clippy::print_stdout, clippy::print_stderr)]
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
@@ -97,12 +98,13 @@ pub(crate) fn main(args: &Args) -> Result<()> {
fn process_documentation(documentation: &str, out: &mut String, rule_name: &str) {
let mut in_options = false;
let mut after = String::new();
let mut referenced_options = HashSet::new();
// HACK: This is an ugly regex hack that's necessary because mkdocs uses
// a non-CommonMark-compliant Markdown parser, which doesn't support code
// tags in link definitions
// (see https://github.com/Python-Markdown/markdown/issues/280).
let documentation = Regex::new(r"\[`([^`]*?)`]($|[^\[])").unwrap().replace_all(
let documentation = Regex::new(r"\[`([^`]*?)`]($|[^\[(])").unwrap().replace_all(
documentation,
|caps: &Captures| {
format!(
@@ -135,6 +137,7 @@ fn process_documentation(documentation: &str, out: &mut String, rule_name: &str)
let anchor = option.replace('.', "_");
out.push_str(&format!("- [`{option}`][{option}]\n"));
after.push_str(&format!("[{option}]: ../settings.md#{anchor}\n"));
referenced_options.insert(option);
continue;
}
@@ -142,6 +145,20 @@ fn process_documentation(documentation: &str, out: &mut String, rule_name: &str)
out.push_str(line);
}
let re = Regex::new(r"\[`([^`]*?)`]\[(.*?)]").unwrap();
for (_, [option, _]) in re.captures_iter(&documentation).map(|c| c.extract()) {
if let Some(OptionEntry::Field(field)) = Options::metadata().find(option) {
if referenced_options.insert(option) {
let anchor = option.replace('.', "_");
after.push_str(&format!("[{option}]: ../settings.md#{anchor}\n"));
}
if field.deprecated.is_some() {
eprintln!("Rule {rule_name} references deprecated option {option}.");
}
}
}
if !after.is_empty() {
out.push('\n');
out.push('\n');
@@ -159,7 +176,7 @@ mod tests {
process_documentation(
"
See also [`lint.mccabe.max-complexity`] and [`lint.task-tags`].
Something [`else`][other].
Something [`else`][other]. Some [link](https://example.com).
## Options
@@ -174,7 +191,7 @@ Something [`else`][other].
output,
"
See also [`lint.mccabe.max-complexity`][lint.mccabe.max-complexity] and [`lint.task-tags`][lint.task-tags].
Something [`else`][other].
Something [`else`][other]. Some [link](https://example.com).
## Options

View File

@@ -180,8 +180,22 @@ pub(crate) fn generate() -> String {
.map(|rule| (rule.upstream_category(&linter), rule))
.into_group_map();
let mut rules_by_upstream_category: Vec<_> = rules_by_upstream_category.iter().collect();
// Sort the upstream categories alphabetically by prefix.
rules_by_upstream_category.sort_by(|(a, _), (b, _)| {
a.as_ref()
.map(|category| category.prefix)
.unwrap_or_default()
.cmp(
b.as_ref()
.map(|category| category.prefix)
.unwrap_or_default(),
)
});
if rules_by_upstream_category.len() > 1 {
for (opt, rules) in &rules_by_upstream_category {
for (opt, rules) in rules_by_upstream_category {
if opt.is_some() {
let UpstreamCategoryAndPrefix { category, prefix } = opt.unwrap();
table_out.push_str(&format!("#### {category} ({prefix})"));

View File

@@ -25,11 +25,6 @@ pub trait Violation: Debug + PartialEq + Eq {
/// The message used to describe the violation.
fn message(&self) -> String;
/// The explanation used in documentation and elsewhere.
fn explanation() -> Option<&'static str> {
None
}
// TODO(micha): Move `fix_title` to `Fix`, add new `advice` method that is shown as an advice.
// Change the `Diagnostic` renderer to show the advice, and render the fix message after the `Suggested fix: <here>`
@@ -50,11 +45,6 @@ pub trait AlwaysFixableViolation: Debug + PartialEq + Eq {
/// The message used to describe the violation.
fn message(&self) -> String;
/// The explanation used in documentation and elsewhere.
fn explanation() -> Option<&'static str> {
None
}
/// The title displayed for the available fix.
fn fix_title(&self) -> String;
@@ -71,10 +61,6 @@ impl<V: AlwaysFixableViolation> Violation for V {
<Self as AlwaysFixableViolation>::message(self)
}
fn explanation() -> Option<&'static str> {
<Self as AlwaysFixableViolation>::explanation()
}
fn fix_title(&self) -> Option<String> {
Some(<Self as AlwaysFixableViolation>::fix_title(self))
}

View File

@@ -24,7 +24,6 @@ tracing = { workspace = true }
unicode-width = { workspace = true }
[dev-dependencies]
insta = { workspace = true }
[features]
serde = ["dep:serde", "ruff_text_size/serde"]

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_linter"
version = "0.3.3"
version = "0.3.5"
publish = false
authors = { workspace = true }
edition = { workspace = true }
@@ -15,7 +15,6 @@ license = { workspace = true }
[dependencies]
ruff_cache = { path = "../ruff_cache" }
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
ruff_index = { path = "../ruff_index" }
ruff_notebook = { path = "../ruff_notebook" }
ruff_macros = { path = "../ruff_macros" }
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
@@ -31,11 +30,11 @@ ruff_text_size = { path = "../ruff_text_size" }
aho-corasick = { workspace = true }
annotate-snippets = { workspace = true, features = ["color"] }
anstream = { workspace = true }
anyhow = { workspace = true }
bitflags = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "string"], optional = true }
colored = { workspace = true }
fern = { workspace = true }
glob = { workspace = true }
globset = { workspace = true }
@@ -48,6 +47,7 @@ log = { workspace = true }
memchr = { workspace = true }
natord = { workspace = true }
once_cell = { workspace = true }
owo-colors = { workspace = true }
path-absolutize = { workspace = true, features = [
"once_cell_cache",
"use_unix_paths_on_wasm",
@@ -75,11 +75,7 @@ url = { workspace = true }
[dev-dependencies]
insta = { workspace = true }
pretty_assertions = { workspace = true }
test-case = { workspace = true }
# Disable colored output in tests
colored = { workspace = true, features = ["no-color"] }
tempfile = { workspace = true }
[features]
default = []

View File

@@ -36,3 +36,32 @@ dictionary = {
# except:
# except Foo:
# except Exception as e: print(e)
# Script tag without an opening tag (Error)
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
# Script tag (OK)
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
# Script tag without a closing tag (OK)
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]

View File

@@ -31,8 +31,7 @@ def function(
kwonly_nonboolvalued_boolhint: bool = 1,
kwonly_nonboolvalued_boolstrhint: "bool" = 1,
**kw,
):
...
): ...
def used(do):
@@ -131,4 +130,27 @@ class Fit:
def __post_init__(self, force: bool) -> None:
print(force)
Fit(force=True)
# https://github.com/astral-sh/ruff/issues/10356
from django.db.models import Case, Q, Value, When
qs.annotate(
is_foo_or_bar=Case(
When(Q(is_foo=True) | Q(is_bar=True)),
then=Value(True),
),
default=Value(False),
)
# https://github.com/astral-sh/ruff/issues/10485
from pydantic import Field
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
foo: bool = Field(True, exclude=True)

View File

@@ -9,62 +9,69 @@ B030:
try:
pass
except 1: # error
except 1: # Error
pass
try:
pass
except (1, ValueError): # error
except (1, ValueError): # Error
pass
try:
pass
except (ValueError, (RuntimeError, (KeyError, TypeError))): # error
except (ValueError, (RuntimeError, (KeyError, TypeError))): # Error
pass
try:
pass
except (ValueError, *(RuntimeError, (KeyError, TypeError))): # error
except (ValueError, *(RuntimeError, (KeyError, TypeError))): # Error
pass
try:
pass
except (*a, *(RuntimeError, (KeyError, TypeError))): # error
pass
try:
pass
except (ValueError, *(RuntimeError, TypeError)): # ok
pass
try:
pass
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
except (*a, *(RuntimeError, (KeyError, TypeError))): # Error
pass
try:
pass
except (*a, *b): # ok
except* a + (RuntimeError, (KeyError, TypeError)): # Error
pass
try:
pass
except (*a, *(RuntimeError, TypeError)): # ok
except (ValueError, *(RuntimeError, TypeError)): # OK
pass
try:
pass
except (ValueError, *[RuntimeError, *(TypeError,)]): # OK
pass
try:
pass
except (*a, *(b, c)): # ok
except (*a, *b): # OK
pass
try:
pass
except (*a, *(*b, *c)): # ok
except (*a, *(RuntimeError, TypeError)): # OK
pass
try:
pass
except (*a, *(b, c)): # OK
pass
try:
pass
except (*a, *(*b, *c)): # OK
pass
@@ -74,5 +81,52 @@ def what_to_catch():
try:
pass
except what_to_catch(): # ok
except what_to_catch(): # OK
pass
try:
pass
except (a, b) + (c, d): # OK
pass
try:
pass
except* (a, b) + (c, d): # OK
pass
try:
pass
except* (a, (b) + (c)): # OK
pass
try:
pass
except (a, b) + (c, d) + (e, f): # OK
pass
try:
pass
except a + (b, c): # OK
pass
try:
pass
except (ValueError, *(RuntimeError, TypeError), *((ArithmeticError,) + (EOFError,))):
pass
try:
pass
except ((a, b) + (c, d)) + ((e, f) + (g)): # OK
pass
try:
pass
except (a, b) * (c, d): # B030
pass

View File

@@ -174,6 +174,49 @@ for (_key1, _key2), (_value1, _value2) in groupby(
collect_shop_items("Jane", group[1])
collect_shop_items("Joe", group[1])
# Shouldn't trigger the warning when there is a continue, break statement.
for _section, section_items in groupby(items, key=lambda p: p[1]):
if _section == "greens":
collect_shop_items(shopper, section_items)
continue
elif _section == "frozen items":
collect_shop_items(shopper, section_items)
break
collect_shop_items(shopper, section_items)
# Shouldn't trigger the warning when there is a return statement.
for _section, section_items in groupby(items, key=lambda p: p[1]):
if _section == "greens":
collect_shop_items(shopper, section_items)
return
elif _section == "frozen items":
return section_items
collect_shop_items(shopper, section_items)
# Should trigger the warning for duplicate access, even if is a return statement after.
for _section, section_items in groupby(items, key=lambda p: p[1]):
if _section == "greens":
collect_shop_items(shopper, section_items)
collect_shop_items(shopper, section_items)
return
# Should trigger the warning for duplicate access, even if is a return in another branch.
for _section, section_items in groupby(items, key=lambda p: p[1]):
if _section == "greens":
collect_shop_items(shopper, section_items)
return
elif _section == "frozen items":
collect_shop_items(shopper, section_items)
collect_shop_items(shopper, section_items)
# Should trigger, since only one branch has a return statement.
for _section, section_items in groupby(items, key=lambda p: p[1]):
if _section == "greens":
collect_shop_items(shopper, section_items)
return
elif _section == "frozen items":
collect_shop_items(shopper, section_items)
collect_shop_items(shopper, section_items) # B031
# Let's redefine the `groupby` function to make sure we pick up the correct one.
# NOTE: This should always be at the end of the file.

View File

@@ -1,20 +1,30 @@
x = set(x for x in range(3))
x = set(x for x in range(3))
y = f"{set(a if a < 6 else 0 for a in range(3))}"
_ = "{}".format(set(a if a < 6 else 0 for a in range(3)))
print(f"Hello {set(a for a in range(3))} World")
# Cannot conbime with C416. Should use set comprehension here.
even_nums = set(2 * x for x in range(3))
odd_nums = set(
2 * x + 1 for x in range(3)
)
small_nums = f"{set(a if a < 6 else 0 for a in range(3))}"
def f(x):
return x
print(f'Hello {set(a for a in "abc")} World')
print(f"Hello {set(a for a in 'abc')} World")
print(f"Hello {set(f(a) for a in 'abc')} World")
print(f"Hello { set(f(a) for a in 'abc') } World")
# Short-circuit case, combine with C416 and should produce x = set(range(3))
x = set(x for x in range(3))
x = set(
x for x in range(3)
)
print(f"Hello {set(a for a in range(3))} World")
print(f"{set(a for a in 'abc') - set(a for a in 'ab')}")
print(f"{ set(a for a in 'abc') - set(a for a in 'ab') }")
# The fix generated for this diagnostic is incorrect, as we add additional space
# around the set comprehension.
print(f"{ {set(a for a in 'abc')} }")
# Not built-in set.
def set(*args, **kwargs):
return None
set(2 * x for x in range(3))
set(x for x in range(3))

View File

@@ -16,3 +16,11 @@ tuple( # comment
tuple([ # comment
1, 2
])
tuple((
1,
))
t6 = tuple([1])
t7 = tuple((1,))
t8 = tuple([1,])

View File

@@ -13,6 +13,10 @@ all(x.id for x in bar)
all(x.id for x in bar)
any(x.id for x in bar)
all((x.id for x in bar))
# we don't lint on these in stable yet
sum([x.val for x in bar])
min([x.val for x in bar])
max([x.val for x in bar])
async def f() -> bool:

View File

@@ -0,0 +1,8 @@
sum([x.val for x in bar])
min([x.val for x in bar])
max([x.val for x in bar])
# Ok
sum(x.val for x in bar)
min(x.val for x in bar)
max(x.val for x in bar)

View File

@@ -0,0 +1,3 @@
# no lint if shadowed
def all(x): pass
all([x.id for x in bar])

View File

@@ -33,3 +33,9 @@ from datetime import datetime
# no replace orastimezone unqualified
datetime.strptime("something", "something")
# F-strings
datetime.strptime("something", f"%Y-%m-%dT%H:%M:%S{('.%f' if millis else '')}%z")
datetime.strptime("something", f"%Y-%m-%d %H:%M:%S%z")
# F-string is implicitly concatenated to another string
datetime.strptime("something", f"%Y-%m-%dT%H:%M:%S{('.%f' if millis else '')}" "%z")

View File

@@ -21,6 +21,7 @@ def unconventional_aliases():
import tkinter as tkr
import networkx as nxy
def conventional_aliases():
import altair as alt
import matplotlib.pyplot as plt

View File

@@ -0,0 +1,10 @@
def no_alias():
from django.conf import settings
def conventional_alias():
from django.conf import settings as settings
def unconventional_alias():
from django.conf import settings as s

View File

@@ -1,9 +1,12 @@
import logging
def func():
import logging
logging.WARN # LOG009
logging.WARNING # OK
logging.WARN # LOG009
logging.WARNING # OK
from logging import WARN, WARNING
WARN # LOG009
WARNING # OK
def func():
from logging import WARN, WARNING
WARN # LOG009
WARNING # OK

View File

@@ -0,0 +1,14 @@
"""Tests to ensure we correctly rename references inside `__all__`"""
from collections.abc import Set
__all__ = ["Set"]
if True:
__all__ += [r'''Set''']
if 1:
__all__ += ["S" "e" "t"]
if not False:
__all__ += ["Se" 't']

View File

@@ -0,0 +1,14 @@
"""Tests to ensure we correctly rename references inside `__all__`"""
from collections.abc import Set
__all__ = ["Set"]
if True:
__all__ += [r'''Set''']
if 1:
__all__ += ["S" "e" "t"]
if not False:
__all__ += ["Se" 't']

View File

@@ -0,0 +1,6 @@
"""
Tests for PYI025 where the import is marked as re-exported
through usage of a "redundant" `import Set as Set` alias
"""
from collections.abc import Set as Set # PYI025 triggered but fix is not marked as safe

View File

@@ -0,0 +1,6 @@
"""
Tests for PYI025 where the import is marked as re-exported
through usage of a "redundant" `import Set as Set` alias
"""
from collections.abc import Set as Set # PYI025 triggered but fix is not marked as safe

View File

@@ -79,5 +79,6 @@ def test_single_list_of_lists(param):
@pytest.mark.parametrize("a", [1, 2])
@pytest.mark.parametrize(("b", "c"), ((3, 4), (5, 6)))
@pytest.mark.parametrize("d", [3,])
def test_multiple_decorators(a, b, c):
pass

View File

@@ -51,3 +51,8 @@ def test_error_parentheses_trailing_comma(x):
@pytest.mark.parametrize("x", [1, 2])
def test_ok(x):
...
@pytest.mark.parametrize('data, spec', [(1.0, 1.0), (1.0, 1.0)])
def test_numbers(data, spec):
...

View File

@@ -0,0 +1,9 @@
class SingleLineDocstrings():
""'Start with empty string' ' and lint docstring safely'
""" Not a docstring """
def foo(self, bar="""not a docstring"""):
""'Start with empty string' ' and lint docstring safely'
pass
class Nested(foo()[:]): ""'Start with empty string' ' and lint docstring safely'; pass

View File

@@ -0,0 +1,9 @@
class SingleLineDocstrings():
"Do not"' start with empty string' ' and lint docstring safely'
""" Not a docstring """
def foo(self, bar="""not a docstring"""):
"Do not"' start with empty string' ' and lint docstring safely'
pass
class Nested(foo()[:]): "Do not"' start with empty string' ' and lint docstring safely'; pass

View File

@@ -0,0 +1,5 @@
""'Start with empty string' ' and lint docstring safely'
def foo():
pass
""" this is not a docstring """

View File

@@ -0,0 +1,5 @@
"Do not"' start with empty string' ' and lint docstring safely'
def foo():
pass
""" this is not a docstring """

View File

@@ -0,0 +1,9 @@
class SingleLineDocstrings():
''"Start with empty string" ' and lint docstring safely'
''' Not a docstring '''
def foo(self, bar='''not a docstring'''):
''"Start with empty string" ' and lint docstring safely'
pass
class Nested(foo()[:]): ''"Start with empty string" ' and lint docstring safely'; pass

View File

@@ -0,0 +1,9 @@
class SingleLineDocstrings():
'Do not'" start with empty string" ' and lint docstring safely'
''' Not a docstring '''
def foo(self, bar='''not a docstring'''):
'Do not'" start with empty string" ' and lint docstring safely'
pass
class Nested(foo()[:]): 'Do not'" start with empty string" ' and lint docstring safely'; pass

View File

@@ -0,0 +1,5 @@
''"Start with empty string" ' and lint docstring safely'
def foo():
pass
""" this is not a docstring """

View File

@@ -0,0 +1,5 @@
'Do not'" start with empty string" ' and lint docstring safely'
def foo():
pass
""" this is not a docstring """

View File

@@ -0,0 +1,7 @@
"""This is a docstring."""
this_is_an_inline_string = "double quote string"
this_is_a_multiline_string = """
double quote string
"""

View File

@@ -0,0 +1,2 @@
s = ""'Start with empty string' ' and lint docstring safely'
s = "Do not"' start with empty string' ' and lint docstring safely'

View File

@@ -2,3 +2,6 @@ this_should_be_linted = 'single quote string'
this_should_be_linted = u'double quote string'
this_should_be_linted = f'double quote string'
this_should_be_linted = f'double {"quote"} string'
# https://github.com/astral-sh/ruff/issues/10546
x: "Literal['foo', 'bar']"

View File

@@ -0,0 +1,2 @@
s = ''"Start with empty string" ' and lint docstring safely'
s = 'Do not'" start with empty string" ' and lint docstring safely'

View File

@@ -406,3 +406,18 @@ def foo():
with contextlib.suppress(Exception):
y = 2
return y
# See: https://github.com/astral-sh/ruff/issues/10732
def func(a: dict[str, int]) -> list[dict[str, int]]:
services: list[dict[str, int]]
if "services" in a:
services = a["services"]
return services
# See: https://github.com/astral-sh/ruff/issues/10732
def func(a: dict[str, int]) -> list[dict[str, int]]:
if "services" in a:
services = a["services"]
return services

View File

@@ -84,3 +84,22 @@ def f():
return True
else:
return False
###
# Positive cases (preview)
###
def f():
# SIM103
if a:
return True
return False
def f():
# SIM103
if a:
return False
return True

View File

@@ -3,3 +3,5 @@ import ruff
import leading_prefix
import os
from . import leading_prefix
from .. import trailing_prefix
from ruff import check

View File

@@ -0,0 +1,6 @@
from astropy.constants import hbar as
from numpy import pi as π
import numpy as ℂℇℊℋℌℍℎℐℑℒℓℕℤΩℨKÅℬℭℯℰℱℹℴ
import numpy as CƐgHHHhIILlNZΩZKÅBCeEFio
h = 2 * π *

View File

@@ -104,3 +104,5 @@ def func():
np.unicode_("asf")
np.who()
np.row_stack(([1,2], [3,4]))

View File

@@ -21,3 +21,10 @@ class D(TypedDict):
mixedCase: bool
_mixedCase: list
mixed_Case: set
class E(D):
lower: int
CONSTANT: str
mixedCase: bool
_mixedCase: list
mixed_Case: set

View File

@@ -47,4 +47,60 @@ snapshot.file_uri[len(f's3://{self.s3_bucket_name}/'):]
{len(f's3://{self.s3_bucket_name}/'):1}
#: Okay
a = (1,
a = (1,)
# https://github.com/astral-sh/ruff/issues/10113
"""Minimal repo."""
def main() -> None:
"""Primary function."""
results = {
"k1": [1],
"k2":[2],
}
results_in_tuple = (
{
"k1": [1],
"k2":[2],
},
)
results_in_list = [
{
"k1": [1],
"k2":[2],
}
]
results_in_list_first = [
{
"k2":[2],
}
]
x = [
{
"k1":[2], # E231
"k2": [2:4],
"k3":[2], # E231
"k4": [2],
"k5": [2],
"k6": [1, 2, 3, 4,5,6,7] # E231
},
{
"k1": [
{
"ka":[2,3], # E231
},
{
"kb": [2,3], # E231
},
{
"ka":[2, 3], # E231
"kb": [2, 3], # Ok
"kc": [2, 3], # Ok
"kd": [2,3], # E231
"ke":[2,3], # E231
},
]
}
]

View File

@@ -0,0 +1,228 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "palRUQyD-U6u"
},
"outputs": [],
"source": [
"some_string = \"123123\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "UWdDLRyf-Zz0"
},
"outputs": [],
"source": [
"some_computation = 1 + 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "YreT1sTr-c32"
},
"outputs": [],
"source": [
"some_computation"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "V48ppml7-h0f"
},
"outputs": [],
"source": [
"def fn():\n",
" print(\"Hey!\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "cscw_8Xv-lYQ"
},
"outputs": [],
"source": [
"fn()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E301\n",
"class Class:\n",
" \"\"\"Class for minimal repo.\"\"\"\n",
"\n",
" def method(cls) -> None:\n",
" pass\n",
" @classmethod\n",
" def cls_method(cls) -> None:\n",
" pass\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E302\n",
"def a():\n",
" pass\n",
"\n",
"def b():\n",
" pass\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E303\n",
"def fn():\n",
" _ = None\n",
"\n",
"\n",
" # arbitrary comment\n",
"\n",
" def inner(): # E306 not expected (pycodestyle detects E306)\n",
" pass\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E303"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"\n",
"\n",
"def fn():\n",
"\tpass\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E304\n",
"@decorator\n",
"\n",
"def function():\n",
" pass\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E305:7:1\n",
"def fn():\n",
" print()\n",
"\n",
" # comment\n",
"\n",
" # another comment\n",
"fn()\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# E306:3:5\n",
"def a():\n",
" x = 1\n",
" def b():\n",
" pass\n",
"# end"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Ok\n",
"def function1():\n",
"\tpass\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"def function2():\n",
"\tpass\n",
"# end"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"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.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@@ -87,6 +87,37 @@
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "a51463ee-091c-44b4-9069-c03bf7e3bf83",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"import pathlib"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0ddc937e-6c19-475f-b108-9405aa1af4f1",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "285041d2-a76c-4ff3-8ff2-0131bbf66016",
"metadata": {},
"outputs": [],
"source": [
"%%time\n",
"%%time\n",
"import pathlib"
]
}
],
"metadata": {

View File

@@ -82,3 +82,8 @@ class Bar:
"""
This is a long sentence that ends with a shortened URL and, therefore, could easily be broken across multiple lines ([source](https://ruff.rs))
"""
# OK
# SPDX-FileCopyrightText: Copyright 2012-2015 Charlie Marsh <very-long-email-address@fake.com>
# SPDX-License-Identifier: a very long license identifier that exceeds the line length limit

View File

@@ -60,7 +60,7 @@ class Scope:
class Scope:
from typing import Callable
# E731
# OK
f: Callable[[int], int] = lambda x: 2 * x
@@ -147,3 +147,12 @@ def scope():
f = lambda: (
i := 1,
)
from dataclasses import dataclass
from typing import Callable
@dataclass
class FilterDataclass:
# OK
filter: Callable[[str], bool] = lambda _: True

View File

@@ -52,3 +52,8 @@ value = rf'\{{1}}'
value = rf'\{1}'
value = rf'{1:\}'
value = f"{rf"\{1}"}"
# Regression tests for https://github.com/astral-sh/ruff/issues/10434
f"{{}}+-\d"
f"\n{{}}+-\d+"
f"\n{{}}<EFBFBD>+-\d+"

View File

@@ -0,0 +1,8 @@
"""Regression test for: https://github.com/astral-sh/ruff/issues/10509"""
from foo import Bar as Bar
class Eggs:
Bar: int # OK
Bar = 1 # F811

View File

@@ -0,0 +1,9 @@
"""Test that unicode identifiers are NFKC-normalised"""
𝒞 = 500
print(𝒞)
print(C + 𝒞) # 2 references to the same variable due to NFKC normalization
print(C / 𝒞)
print(C == 𝑪 == 𝒞 == 𝓒 == 𝕮)
print(𝒟) # F821

View File

@@ -0,0 +1,23 @@
"""Regression test for #10451.
Annotations in a class are allowed to be forward references
if `from __future__ import annotations` is active,
even if they're in a class included in
`lint.flake8-type-checking.runtime-evaluated-base-classes`.
They're not allowed to refer to symbols that cannot be *resolved*
at runtime, however.
"""
from __future__ import annotations
from sqlalchemy.orm import DeclarativeBase, Mapped
class Base(DeclarativeBase):
some_mapping: Mapped[list[Bar]] | None = None # Should not trigger F821 (resolveable forward reference)
simplified: list[Bar] | None = None # Should not trigger F821 (resolveable forward reference)
class Bar:
pass

View File

@@ -5,6 +5,7 @@ class Person: # [eq-without-hash]
def __eq__(self, other):
return isinstance(other, Person) and other.name == self.name
# OK
class Language:
def __init__(self):
@@ -16,8 +17,24 @@ class Language:
def __hash__(self):
return hash(self.name)
class MyClass:
def __eq__(self, other):
return True
__hash__ = None
class SingleClass:
def __eq__(self, other):
return True
def __hash__(self):
return 7
class ChildClass(SingleClass):
def __eq__(self, other):
return True
__hash__ = SingleClass.__hash__

View File

@@ -0,0 +1,60 @@
# Errors
nums = {1, 2, 3}
for num in nums:
nums.add(num + 1)
animals = {"dog", "cat", "cow"}
for animal in animals:
animals.pop("cow")
fruits = {"apple", "orange", "grape"}
for fruit in fruits:
fruits.clear()
planets = {"mercury", "venus", "earth"}
for planet in planets:
planets.discard("mercury")
colors = {"red", "green", "blue"}
for color in colors:
colors.remove("red")
odds = {1, 3, 5}
for num in odds:
if num > 1:
odds.add(num + 1)
# OK
nums = {1, 2, 3}
for num in nums.copy():
nums.add(nums + 3)
animals = {"dog", "cat", "cow"}
for animal in animals:
print(animals - {animal})
fruits = {"apple", "orange", "grape"}
temp_fruits = set()
for fruit in fruits:
temp_fruits.add(fruit)
temp_fruits.remove(fruit)
temp_fruits.clear(fruit)
colors = {"red", "green", "blue"}
def add_colors():
colors = {"cyan", "magenta", "yellow"}
for color in colors:
def add_color():
global colors
colors.add(color)
add_color()
add_colors()
print(colors)

View File

@@ -0,0 +1,76 @@
import math
from math import nan as bad_val
import numpy as np
from numpy import nan as npy_nan
x = float("nan")
y = np.NaN
# PLW0117
if x == float("nan"):
pass
# PLW0117
if x == float("NaN"):
pass
# PLW0117
if x == float("NAN"):
pass
# PLW0117
if x == float("Nan"):
pass
# PLW0117
if x == math.nan:
pass
# PLW0117
if x == bad_val:
pass
# PLW0117
if y == np.NaN:
pass
# PLW0117
if y == np.NAN:
pass
# PLW0117
if y == np.nan:
pass
# PLW0117
if y == npy_nan:
pass
# OK
if math.isnan(x):
pass
# OK
if np.isnan(y):
pass
# OK
if x == 0:
pass
# OK
if x == float("32"):
pass
# OK
if x == float(42):
pass
# OK
if y == np.inf:
pass
# OK
if x == "nan":
pass

View File

@@ -0,0 +1,67 @@
# Positive cases
counter = 0
def count():
global counter
nonlocal counter
counter += 1
def count():
counter = 0
def count(counter_type):
if counter_type == "nonlocal":
nonlocal counter
counter += 1
else:
global counter
counter += 1
def count():
counter = 0
def count_twice():
for i in range(2):
nonlocal counter
counter += 1
global counter
def count():
nonlocal counter
global counter
counter += 1
# Negative cases
counter = 0
def count():
global counter
counter += 1
def count():
counter = 0
def count_local():
nonlocal counter
counter += 1
def count():
counter = 0
def count_local():
nonlocal counter
counter += 1
def count_global():
global counter
counter += 1

View File

@@ -20,7 +20,7 @@ class Board:
def place(self, position):
pass
@singledispatch
@singledispatch # [singledispatch-method]
@staticmethod
def do(position):
pass

View File

@@ -0,0 +1,23 @@
from functools import singledispatchmethod
@singledispatchmethod # [singledispatchmethod-function]
def convert_position(position):
pass
class Board:
@singledispatchmethod # Ok
@classmethod
def convert_position(cls, position):
pass
@singledispatchmethod # Ok
def move(self, position):
pass
@singledispatchmethod # Ok
@staticmethod
def do(position):
pass

View File

@@ -1,11 +1,28 @@
import datetime
import datetime as dt
from datetime import timezone
from datetime import timezone as tz
def func():
import datetime
print(datetime.timezone(-1))
print(timezone.utc)
print(tz.utc)
print(datetime.timezone(-1))
print(datetime.timezone.utc)
print(dt.timezone.utc)
def func():
from datetime import timezone
print(timezone.utc)
def func():
from datetime import timezone as tz
print(tz.utc)
def func():
import datetime
print(datetime.timezone.utc)
def func():
import datetime as dt
print(dt.timezone.utc)

View File

@@ -0,0 +1,61 @@
# Errors
s = set()
for x in [1, 2, 3]:
s.add(x)
for x in {1, 2, 3}:
s.add(x)
for x in (1, 2, 3):
s.add(x)
for x in (1, 2, 3):
s.discard(x)
for x in (1, 2, 3):
s.add(x + 1)
for x, y in ((1, 2), (3, 4)):
s.add((x, y))
num = 123
for x in (1, 2, 3):
s.add(num)
for x in (1, 2, 3):
s.add((num, x))
for x in (1, 2, 3):
s.add(x + num)
# False negative
class C:
s: set[int]
c = C()
for x in (1, 2, 3):
c.s.add(x)
# Ok
s.update(x for x in (1, 2, 3))
for x in (1, 2, 3):
s.add(x)
else:
pass
async def f(y):
async for x in y:
s.add(x)
def g():
for x in (set(),):
x.add(x)

View File

@@ -0,0 +1,17 @@
import decimal
from decimal import Decimal
# Errors
Decimal("0")
Decimal("-42")
Decimal(float("Infinity"))
Decimal(float("-Infinity"))
Decimal(float("inf"))
Decimal(float("-inf"))
Decimal(float("nan"))
decimal.Decimal("0")
# OK
Decimal(0)
Decimal("Infinity")
decimal.Decimal(0)

View File

@@ -1,9 +1,6 @@
import math
from math import e as special_e
from math import log as special_log
# Errors.
# Errors
math.log(1, 2)
math.log(1, 10)
math.log(1, math.e)
@@ -11,15 +8,10 @@ foo = ...
math.log(foo, 2)
math.log(foo, 10)
math.log(foo, math.e)
math.log(1, special_e)
special_log(1, 2)
special_log(1, 10)
special_log(1, math.e)
special_log(1, special_e)
math.log(1, 2.0)
math.log(1, 10.0)
# Ok.
# OK
math.log2(1)
math.log10(1)
math.log(1)
@@ -40,6 +32,7 @@ math.log10(1, 2) # math.log10 takes only one argument.
math.log(1, base=2) # math.log does not accept keyword arguments.
def log(*args):
print(f"Logging: {args}")

View File

@@ -0,0 +1,34 @@
from decimal import Decimal
from fractions import Fraction
import decimal
import fractions
# Errors
_ = Fraction.from_float(0.1)
_ = Fraction.from_float(-0.5)
_ = Fraction.from_float(5.0)
_ = fractions.Fraction.from_float(4.2)
_ = Fraction.from_decimal(Decimal("4.2"))
_ = Fraction.from_decimal(Decimal("-4.2"))
_ = Fraction.from_decimal(Decimal.from_float(4.2))
_ = Decimal.from_float(0.1)
_ = Decimal.from_float(-0.5)
_ = Decimal.from_float(5.0)
_ = decimal.Decimal.from_float(4.2)
_ = Decimal.from_float(float("inf"))
_ = Decimal.from_float(float("-inf"))
_ = Decimal.from_float(float("Infinity"))
_ = Decimal.from_float(float("-Infinity"))
_ = Decimal.from_float(float("nan"))
# OK
_ = Fraction(0.1)
_ = Fraction(-0.5)
_ = Fraction(5.0)
_ = fractions.Fraction(4.2)
_ = Fraction(Decimal("4.2"))
_ = Fraction(Decimal("-4.2"))
_ = Decimal(0.1)
_ = Decimal(-0.5)
_ = Decimal(5.0)
_ = decimal.Decimal(4.2)

View File

@@ -0,0 +1,29 @@
# Errors
_ = int("0b1010"[2:], 2)
_ = int("0o777"[2:], 8)
_ = int("0xFFFF"[2:], 16)
b = "0b11"
_ = int(b[2:], 2)
_ = int("0xFFFF"[2:], base=16)
_ = int(b"0xFFFF"[2:], 16)
def get_str():
return "0xFFF"
_ = int(get_str()[2:], 16)
# OK
_ = int("0b1100", 0)
_ = int("123", 3)
_ = int("123", 10)
_ = int("0b1010"[3:], 2)
_ = int("0b1010"[:2], 2)
_ = int("12345"[2:])
_ = int("12345"[2:], xyz=1) # type: ignore

View File

@@ -0,0 +1,62 @@
# Errors
def a():
l = []
l = reversed(l)
def b():
l = []
l = list(reversed(l))
def c():
l = []
l = l[::-1]
# False negative
def c2():
class Wrapper:
l: list[int]
w = Wrapper()
w.l = list(reversed(w.l))
w.l = w.l[::-1]
w.l = reversed(w.l)
# OK
def d():
l = []
_ = reversed(l)
def e():
l = []
l = l[::-2]
l = l[1:]
l = l[1::-1]
l = l[:1:-1]
def f():
d = {}
# Don't warn: `d` is a dictionary, which doesn't have a `reverse` method.
d = reversed(d)
def g():
l = "abc"[::-1]
def h():
l = reversed([1, 2, 3])
def i():
l = list(reversed([1, 2, 3]))

View File

@@ -1,4 +1,3 @@
import typing
from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
@@ -26,10 +25,6 @@ def f(arg: str = None): # RUF013
pass
def f(arg: typing.List[str] = None): # RUF013
pass
def f(arg: Tuple[str] = None): # RUF013
pass
@@ -41,10 +36,6 @@ def f(arg: Optional[int] = None):
pass
def f(arg: typing.Optional[int] = None):
pass
# Union
@@ -60,10 +51,6 @@ def f(arg: Union[str, None] = None):
pass
def f(arg: typing.Union[int, str, None] = None):
pass
def f(arg: Union[int, str, Any] = None):
pass
@@ -80,10 +67,6 @@ def f(arg: Union[int, str] = None): # RUF013
pass
def f(arg: typing.Union[int, str] = None): # RUF013
pass
# PEP 604 Union
@@ -130,10 +113,6 @@ def f(arg: Literal[1, "foo"] = None): # RUF013
pass
def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
pass
# Annotated

View File

@@ -1,5 +1,5 @@
# No `typing.Optional` import
def f(arg: int = None): # RUF011
def f(arg: int = None): # RUF013
pass

View File

@@ -0,0 +1,30 @@
import typing
def f(arg: typing.List[str] = None): # RUF013
pass
# Optional
def f(arg: typing.Optional[int] = None):
pass
# Union
def f(arg: typing.Union[int, str, None] = None):
pass
def f(arg: typing.Union[int, str] = None): # RUF013
pass
# Literal
def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
pass

View File

@@ -100,3 +100,9 @@ def f():
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
""" # noqa
def f():
# Invalid - nonexistant error code with multibyte character
d = 1 #…noqa: F841, E50
e = 1 #…noqa: E50

View File

@@ -3,13 +3,10 @@ Violation:
Use '.exception' over '.error' inside except blocks
"""
import logging
import sys
logger = logging.getLogger(__name__)
def bad():
import logging
try:
a = 1
except Exception:
@@ -20,6 +17,10 @@ def bad():
def bad():
import logging
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
@@ -50,6 +51,10 @@ def bad():
def good():
import logging
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
@@ -64,6 +69,10 @@ def good():
def fine():
import logging
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
@@ -71,16 +80,20 @@ def fine():
def fine():
import logging
import sys
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
logger.error("Context message here", exc_info=sys.exc_info())
from logging import error, exception
def bad():
from logging import error, exception
try:
a = 1
except Exception:
@@ -91,6 +104,8 @@ def bad():
def good():
from logging import error, exception
try:
a = 1
except Exception:
@@ -98,6 +113,8 @@ def good():
def fine():
from logging import error, exception
try:
a = 1
except Exception:
@@ -105,6 +122,9 @@ def fine():
def fine():
from logging import error, exception
import sys
try:
a = 1
except Exception:
@@ -112,6 +132,8 @@ def fine():
def nested():
from logging import error, exception
try:
a = 1
except Exception:

View File

@@ -43,6 +43,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
Rule::UnusedStaticMethodArgument,
Rule::UnusedVariable,
Rule::SingledispatchMethod,
Rule::SingledispatchmethodFunction,
]) {
return;
}
@@ -419,6 +420,10 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
pylint::rules::singledispatch_method(checker, scope, &mut diagnostics);
}
if checker.enabled(Rule::SingledispatchmethodFunction) {
pylint::rules::singledispatchmethod_function(checker, scope, &mut diagnostics);
}
if checker.any_enabled(&[
Rule::InvalidFirstArgumentNameForClassMethod,
Rule::InvalidFirstArgumentNameForMethod,

View File

@@ -1,4 +1,4 @@
use ruff_python_ast::str::raw_contents_range;
use ruff_python_ast::{all::DunderAllName, str::raw_contents_range};
use ruff_text_size::{Ranged, TextRange};
use ruff_python_semantic::{
@@ -93,7 +93,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
}
// Compute visibility of all definitions.
let exports: Option<Vec<&str>> = {
let exports: Option<Vec<DunderAllName>> = {
checker
.semantic
.global_scope()

View File

@@ -223,14 +223,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
}
}
if checker.enabled(Rule::MixedCaseVariableInClassScope) {
if let ScopeKind::Class(ast::StmtClassDef { arguments, .. }) =
&checker.semantic.current_scope().kind
if let ScopeKind::Class(class_def) = &checker.semantic.current_scope().kind
{
pep8_naming::rules::mixed_case_variable_in_class_scope(
checker,
expr,
id,
arguments.as_deref(),
checker, expr, id, class_def,
);
}
}
@@ -709,8 +705,8 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
args,
);
}
if checker.enabled(Rule::UnnecessaryComprehensionAnyAll) {
flake8_comprehensions::rules::unnecessary_comprehension_any_all(
if checker.enabled(Rule::UnnecessaryComprehensionInCall) {
flake8_comprehensions::rules::unnecessary_comprehension_in_call(
checker, expr, func, args, keywords,
);
}
@@ -926,6 +922,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::RedundantLogBase) {
refurb::rules::redundant_log_base(checker, call);
}
if checker.enabled(Rule::VerboseDecimalConstructor) {
refurb::rules::verbose_decimal_constructor(checker, call);
}
if checker.enabled(Rule::UnnecessaryFromFloat) {
refurb::rules::unnecessary_from_float(checker, call);
}
if checker.enabled(Rule::QuadraticListSummation) {
ruff::rules::quadratic_list_summation(checker, call);
}
@@ -971,6 +973,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::UnnecessaryIterableAllocationForFirstElement) {
ruff::rules::unnecessary_iterable_allocation_for_first_element(checker, expr);
}
if checker.enabled(Rule::IntOnSlicedStr) {
refurb::rules::int_on_sliced_str(checker, call);
}
}
Expr::Dict(dict) => {
if checker.any_enabled(&[
@@ -1283,6 +1288,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::MagicValueComparison) {
pylint::rules::magic_value_comparison(checker, left, comparators);
}
if checker.enabled(Rule::NanComparison) {
pylint::rules::nan_comparison(checker, left, comparators);
}
if checker.enabled(Rule::InDictKeys) {
flake8_simplify::rules::key_in_dict_compare(checker, compare);
}

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