Compare commits

..

1 Commits

Author SHA1 Message Date
konstin
7f97547b5f Add increment/decrement 2024-03-14 16:56:06 +01:00
494 changed files with 31078 additions and 37832 deletions

21
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
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"]

View File

@@ -1,60 +0,0 @@
{
$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@v44
- uses: tj-actions/changed-files@v42
id: changed
with:
files_yaml: |

1
.gitignore vendored
View File

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

View File

@@ -1,134 +1,5 @@
# 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
- \[`flake8-bandit`\]: Implement `S610` rule ([#10316](https://github.com/astral-sh/ruff/pull/10316))
- \[`pycodestyle`\] Implement `blank-line-at-end-of-file` (`W391`) ([#10243](https://github.com/astral-sh/ruff/pull/10243))
- \[`pycodestyle`\] Implement `redundant-backslash` (`E502`) ([#10292](https://github.com/astral-sh/ruff/pull/10292))
- \[`pylint`\] - implement `redeclared-assigned-name` (`W0128`) ([#9268](https://github.com/astral-sh/ruff/pull/9268))
### Rule changes
- \[`flake8_comprehensions`\] Handled special case for `C400` which also matches `C416` ([#10419](https://github.com/astral-sh/ruff/pull/10419))
- \[`flake8-bandit`\] Implement upstream updates for `S311`, `S324` and `S605` ([#10313](https://github.com/astral-sh/ruff/pull/10313))
- \[`pyflakes`\] Remove `F401` fix for `__init__` imports by default and allow opt-in to unsafe fix ([#10365](https://github.com/astral-sh/ruff/pull/10365))
- \[`pylint`\] Implement `invalid-bool-return-type` (`E304`) ([#10377](https://github.com/astral-sh/ruff/pull/10377))
- \[`pylint`\] Include builtin warnings in useless-exception-statement (`PLW0133`) ([#10394](https://github.com/astral-sh/ruff/pull/10394))
### CLI
- Add message on success to `ruff check` ([#8631](https://github.com/astral-sh/ruff/pull/8631))
### Bug fixes
- \[`PIE970`\] Allow trailing ellipsis in `typing.TYPE_CHECKING` ([#10413](https://github.com/astral-sh/ruff/pull/10413))
- Avoid `TRIO115` if the argument is a variable ([#10376](https://github.com/astral-sh/ruff/pull/10376))
- \[`F811`\] Avoid removing shadowed imports that point to different symbols ([#10387](https://github.com/astral-sh/ruff/pull/10387))
- Fix `F821` and `F822` false positives in `.pyi` files ([#10341](https://github.com/astral-sh/ruff/pull/10341))
- Fix `F821` false negatives in `.py` files when `from __future__ import annotations` is active ([#10362](https://github.com/astral-sh/ruff/pull/10362))
- Fix case where `Indexer` fails to identify continuation preceded by newline #10351 ([#10354](https://github.com/astral-sh/ruff/pull/10354))
- Sort hash maps in `Settings` display ([#10370](https://github.com/astral-sh/ruff/pull/10370))
- Track conditional deletions in the semantic model ([#10415](https://github.com/astral-sh/ruff/pull/10415))
- \[`C413`\] Wrap expressions in parentheses when negating ([#10346](https://github.com/astral-sh/ruff/pull/10346))
- \[`pycodestyle`\] Do not ignore lines before the first logical line in blank lines rules. ([#10382](https://github.com/astral-sh/ruff/pull/10382))
- \[`pycodestyle`\] Do not trigger `E225` and `E275` when the next token is a ')' ([#10315](https://github.com/astral-sh/ruff/pull/10315))
- \[`pylint`\] Avoid false-positive slot non-assignment for `__dict__` (`PLE0237`) ([#10348](https://github.com/astral-sh/ruff/pull/10348))
- Gate f-string struct size test for Rustc \< 1.76 ([#10371](https://github.com/astral-sh/ruff/pull/10371))
### Documentation
- Use `ruff.toml` format in README ([#10393](https://github.com/astral-sh/ruff/pull/10393))
- \[`RUF008`\] Make it clearer that a mutable default in a dataclass is only valid if it is typed as a ClassVar ([#10395](https://github.com/astral-sh/ruff/pull/10395))
- \[`pylint`\] Extend docs and test in `invalid-str-return-type` (`E307`) ([#10400](https://github.com/astral-sh/ruff/pull/10400))
- Remove `.` from `check` and `format` commands ([#10217](https://github.com/astral-sh/ruff/pull/10217))
## 0.3.2
### Preview features
@@ -146,7 +17,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
@@ -254,7 +125,8 @@ 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
@@ -902,7 +774,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
@@ -1031,7 +903,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
@@ -1199,7 +1071,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

View File

@@ -33,18 +33,27 @@ 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
@@ -98,7 +107,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
@@ -108,14 +117,7 @@ after running `cargo test` like so:
cargo insta review
```
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
Your Pull Request will be reviewed by a maintainer, which may involve a few rounds of iteration
prior to merging.
### Project Structure
@@ -197,14 +199,11 @@ 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)), and regenerate the documentation and
associated assets (like our JSON Schema) with `cargo dev generate-all`.
Once you're satisfied with your code, add tests for your rule. See [rule testing](#rule-testing-fixtures-and-snapshots)
for more details.
Finally, submit a pull request, and include the category, rule name, and rule code in the title, as
in:
> \[`pycodestyle`\] Implement `redundant-backslash` (`E502`)
Finally, regenerate the documentation and other generated assets (like our JSON Schema) with:
`cargo dev generate-all`.
#### Rule naming convention
@@ -814,8 +813,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 in a subtree
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 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:

359
Cargo.lock generated
View File

@@ -29,9 +29,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
version = "1.1.3"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
@@ -123,9 +123,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.81"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[package]]
name = "argfile"
@@ -152,6 +152,21 @@ dependencies = [
"term",
]
[[package]]
name = "assert_cmd"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
dependencies = [
"anstyle",
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -196,9 +211,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bstr"
@@ -255,9 +270,9 @@ dependencies = [
[[package]]
name = "chrono"
version = "0.4.37"
version = "0.4.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a"
dependencies = [
"android-tzdata",
"iana-time-zone",
@@ -294,9 +309,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.4"
version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651"
dependencies = [
"clap_builder",
"clap_derive",
@@ -358,14 +373,14 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.4"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck 0.5.0",
"heck",
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -596,7 +611,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -607,7 +622,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [
"darling_core",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -616,6 +631,12 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "dirs"
version = "4.0.0"
@@ -678,6 +699,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "drop_bomb"
version = "0.1.5"
@@ -885,12 +912,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
@@ -1020,9 +1041,9 @@ dependencies = [
[[package]]
name = "indoc"
version = "2.0.5"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
[[package]]
name = "inotify"
@@ -1046,9 +1067,9 @@ dependencies = [
[[package]]
name = "insta"
version = "1.38.0"
version = "1.35.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc"
checksum = "7c985c1bef99cf13c58fade470483d81a2bfe846ebde60ed28cc2dddec2df9e2"
dependencies = [
"console",
"globset",
@@ -1058,13 +1079,14 @@ dependencies = [
"serde",
"similar",
"walkdir",
"yaml-rust",
]
[[package]]
name = "insta-cmd"
version = "0.5.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1980f17994b79f75670aa90cfc8d35edc4aa248f16aa48b5e27835b080e452a2"
checksum = "809d3023d1d6e8d5c2206f199251f75cb26180e41f18cb0f22dd119161cb5127"
dependencies = [
"insta",
"serde",
@@ -1098,7 +1120,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -1281,7 +1303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbd2f3cd9346422ebdc3a614aed6969d4e0b3e9c10517f33b30326acf894c11"
dependencies = [
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -1300,7 +1322,7 @@ version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"libc",
"redox_syscall",
]
@@ -1375,9 +1397,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "memchr"
version = "2.7.2"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "mimalloc"
@@ -1460,7 +1482,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@@ -1483,15 +1505,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "nu-ansi-term"
version = "0.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68"
dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "num-traits"
version = "0.2.18"
@@ -1501,16 +1514,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
@@ -1642,18 +1645,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "pep440_rs"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15efd4d885c29126cc93e12af3087896e2518bd5ca0fb328c19c4ef9cecfa8be"
dependencies = [
"once_cell",
"serde",
"unicode-width",
"unscanny",
]
[[package]]
name = "pep508_rs"
version = "0.3.0"
@@ -1661,7 +1652,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "910c513bea0f4f833122321c0f20e8c704e01de98692f6989c2ec21f43d88b1e"
dependencies = [
"once_cell",
"pep440_rs 0.4.0",
"pep440_rs",
"regex",
"serde",
"thiserror",
@@ -1747,7 +1738,7 @@ checksum = "52a40bc70c2c58040d2d8b167ba9a5ff59fc9dab7ad44771cfde3dcfde7a09c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -1768,6 +1759,33 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "predicates"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
dependencies = [
"anstyle",
"difflib",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
[[package]]
name = "predicates-tree"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "pretty_assertions"
version = "1.4.0"
@@ -1780,9 +1798,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.79"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
@@ -1794,7 +1812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95c3dd745f99aa3c554b7bb00859f7d18c2f1d6afd749ccc86d60b61e702abd9"
dependencies = [
"indexmap",
"pep440_rs 0.4.0",
"pep440_rs",
"pep508_rs",
"serde",
"toml",
@@ -1865,9 +1883,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.10.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd"
dependencies = [
"either",
"rayon-core",
@@ -1905,9 +1923,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.4"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
@@ -1965,7 +1983,7 @@ dependencies = [
"pmutil",
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -1985,12 +2003,13 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.3.5"
version = "0.3.2"
dependencies = [
"anyhow",
"argfile",
"assert_cmd",
"bincode",
"bitflags 2.5.0",
"bitflags 2.4.2",
"cachedir",
"chrono",
"clap",
@@ -2006,7 +2025,6 @@ dependencies = [
"log",
"mimalloc",
"notify",
"num_cpus",
"path-absolutize",
"rayon",
"regex",
@@ -2083,6 +2101,7 @@ dependencies = [
"indoc",
"itertools 0.12.1",
"libcst",
"once_cell",
"pretty_assertions",
"rayon",
"regex",
@@ -2126,6 +2145,7 @@ name = "ruff_formatter"
version = "0.0.0"
dependencies = [
"drop_bomb",
"insta",
"ruff_cache",
"ruff_macros",
"ruff_text_size",
@@ -2147,12 +2167,12 @@ dependencies = [
[[package]]
name = "ruff_linter"
version = "0.3.5"
version = "0.3.2"
dependencies = [
"aho-corasick",
"annotate-snippets 0.9.2",
"anyhow",
"bitflags 2.5.0",
"bitflags 2.4.2",
"chrono",
"clap",
"colored",
@@ -2171,13 +2191,15 @@ dependencies = [
"once_cell",
"path-absolutize",
"pathdiff",
"pep440_rs 0.5.0",
"pep440_rs",
"pretty_assertions",
"pyproject-toml",
"quick-junit",
"regex",
"result-like",
"ruff_cache",
"ruff_diagnostics",
"ruff_index",
"ruff_macros",
"ruff_notebook",
"ruff_python_ast",
@@ -2198,6 +2220,7 @@ dependencies = [
"smallvec",
"strum",
"strum_macros",
"tempfile",
"test-case",
"thiserror",
"toml",
@@ -2215,7 +2238,7 @@ dependencies = [
"proc-macro2",
"quote",
"ruff_python_trivia",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -2223,6 +2246,7 @@ name = "ruff_notebook"
version = "0.0.0"
dependencies = [
"anyhow",
"insta",
"itertools 0.12.1",
"once_cell",
"rand",
@@ -2242,7 +2266,7 @@ name = "ruff_python_ast"
version = "0.0.0"
dependencies = [
"aho-corasick",
"bitflags 2.5.0",
"bitflags 2.4.2",
"insta",
"is-macro",
"itertools 0.12.1",
@@ -2271,6 +2295,7 @@ name = "ruff_python_formatter"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags 2.4.2",
"clap",
"countme",
"insta",
@@ -2314,10 +2339,12 @@ dependencies = [
name = "ruff_python_literal"
version = "0.0.0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"hexf-parse",
"is-macro",
"itertools 0.12.1",
"lexical-parse-float",
"rand",
"ruff_python_ast",
"unic-ucd-category",
]
@@ -2327,7 +2354,7 @@ name = "ruff_python_parser"
version = "0.0.0"
dependencies = [
"anyhow",
"bitflags 2.5.0",
"bitflags 2.4.2",
"bstr",
"insta",
"is-macro",
@@ -2341,7 +2368,6 @@ dependencies = [
"static_assertions",
"tiny-keccak",
"unicode-ident",
"unicode-normalization",
"unicode_names2",
]
@@ -2359,7 +2385,7 @@ dependencies = [
name = "ruff_python_semantic"
version = "0.0.0"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"is-macro",
"ruff_index",
"ruff_python_ast",
@@ -2383,6 +2409,7 @@ version = "0.0.0"
dependencies = [
"insta",
"itertools 0.12.1",
"ruff_python_ast",
"ruff_python_index",
"ruff_python_parser",
"ruff_source_file",
@@ -2415,12 +2442,13 @@ dependencies = [
"rustc-hash",
"serde",
"serde_json",
"similar",
"tracing",
]
[[package]]
name = "ruff_shrinking"
version = "0.3.5"
version = "0.3.2"
dependencies = [
"anyhow",
"clap",
@@ -2438,6 +2466,7 @@ dependencies = [
name = "ruff_source_file"
version = "0.0.0"
dependencies = [
"insta",
"memchr",
"once_cell",
"ruff_text_size",
@@ -2492,8 +2521,9 @@ dependencies = [
"is-macro",
"itertools 0.12.1",
"log",
"once_cell",
"path-absolutize",
"pep440_rs 0.5.0",
"pep440_rs",
"regex",
"ruff_cache",
"ruff_formatter",
@@ -2533,7 +2563,7 @@ version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys",
@@ -2662,7 +2692,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -2678,9 +2708,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.115"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
dependencies = [
"itoa",
"ryu",
@@ -2695,7 +2725,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -2718,9 +2748,9 @@ dependencies = [
[[package]]
name = "serde_with"
version = "3.7.0"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a"
checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270"
dependencies = [
"serde",
"serde_derive",
@@ -2729,14 +2759,14 @@ dependencies = [
[[package]]
name = "serde_with_macros"
version = "3.7.0"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655"
checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -2765,9 +2795,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "similar"
version = "2.5.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21"
[[package]]
name = "siphasher"
@@ -2777,9 +2807,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "smallvec"
version = "1.13.2"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "spin"
@@ -2829,24 +2859,24 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "strum"
version = "0.26.2"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck 0.4.1",
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -2868,9 +2898,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.57"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@@ -2932,6 +2962,12 @@ dependencies = [
"phf_codegen",
]
[[package]]
name = "termtree"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "test-case"
version = "3.3.1"
@@ -2950,7 +2986,7 @@ dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -2961,28 +2997,28 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
"test-case-core",
]
[[package]]
name = "thiserror"
version = "1.0.58"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -3051,9 +3087,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.8.12"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290"
dependencies = [
"serde",
"serde_spanned",
@@ -3072,9 +3108,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.9"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6"
dependencies = [
"indexmap",
"serde",
@@ -3103,7 +3139,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -3128,6 +3164,17 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "tracing-log"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2"
dependencies = [
"log",
"once_cell",
"tracing-core",
]
[[package]]
name = "tracing-log"
version = "0.2.0"
@@ -3146,7 +3193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers",
"nu-ansi-term 0.46.0",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
@@ -3154,18 +3201,18 @@ dependencies = [
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-log 0.2.0",
]
[[package]]
name = "tracing-tree"
version = "0.3.0"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65139ecd2c3f6484c3b99bc01c77afe21e95473630747c7aca525e78b0666675"
checksum = "2ec6adcab41b1391b08a308cc6302b79f8095d1673f6947c2dc65ffb028b0b2d"
dependencies = [
"nu-ansi-term 0.49.0",
"nu-ansi-term",
"tracing-core",
"tracing-log",
"tracing-log 0.1.4",
"tracing-subscriber",
]
@@ -3272,12 +3319,6 @@ dependencies = [
"rand",
]
[[package]]
name = "unscanny"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47"
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -3321,9 +3362,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.8.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
dependencies = [
"getrandom",
"rand",
@@ -3333,13 +3374,13 @@ dependencies = [
[[package]]
name = "uuid-macro-internal"
version = "1.8.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9881bea7cbe687e36c9ab3b778c36cd0487402e270304e8b1296d5085303c1a2"
checksum = "7abb14ae1a50dad63eaa768a458ef43d298cd1bd44951677bd10b732a9ba2a2d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -3387,6 +3428,15 @@ dependencies = [
"quote",
]
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "walkdir"
version = "2.5.0"
@@ -3424,15 +3474,15 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
dependencies = [
"cfg-if",
"js-sys",
@@ -3458,7 +3508,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3471,9 +3521,9 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.42"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
checksum = "143ddeb4f833e2ed0d252e618986e18bfc7b0e52f2d28d77d05b2f045dd8eb61"
dependencies = [
"console_error_panic_hook",
"js-sys",
@@ -3485,13 +3535,13 @@ dependencies = [
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.42"
version = "0.3.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
checksum = "a5211b7550606857312bba1d978a8ec75692eae187becc5e680444fffc5e6f89"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]
@@ -3715,6 +3765,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "0.5.1"
@@ -3747,7 +3806,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.57",
"syn 2.0.52",
]
[[package]]

View File

@@ -12,20 +12,22 @@ authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
license = "MIT"
[workspace.dependencies]
aho-corasick = { version = "1.1.3" }
aho-corasick = { version = "1.1.2" }
annotate-snippets = { version = "0.9.2", features = ["color"] }
anyhow = { version = "1.0.80" }
argfile = { version = "0.1.6" }
assert_cmd = { version = "2.0.13" }
bincode = { version = "1.3.3" }
bitflags = { version = "2.5.0" }
bitflags = { version = "2.4.1" }
bstr = { version = "1.9.1" }
cachedir = { version = "0.3.1" }
chrono = { version = "0.4.35", default-features = false, features = ["clock"] }
clap = { version = "4.5.3", features = ["derive"] }
clap = { version = "4.5.2", 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" }
@@ -46,7 +48,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.5.0" }
insta-cmd = { version = "0.4.0" }
is-macro = { version = "0.3.5" }
is-wsl = { version = "0.4.0" }
itertools = { version = "0.12.1" }
@@ -63,18 +65,17 @@ 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" }
path-absolutize = { version = "3.1.1" }
pathdiff = { version = "0.2.1" }
pep440_rs = { version = "0.5.0", features = ["serde"] }
pep440_rs = { version = "0.4.0", features = ["serde"] }
pretty_assertions = "1.3.0"
proc-macro2 = { version = "1.0.79" }
proc-macro2 = { version = "1.0.78" }
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.10.0" }
rayon = { version = "1.8.1" }
regex = { version = "1.10.2" }
result-like = { version = "0.5.0" }
rustc-hash = { version = "1.1.0" }
@@ -88,32 +89,31 @@ 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.2" }
smallvec = { version = "1.13.1" }
static_assertions = "1.1.0"
strum = { version = "0.26.0", features = ["strum_macros"] }
strum_macros = { version = "0.26.0" }
syn = { version = "2.0.55" }
strum = { version = "0.25.0", features = ["strum_macros"] }
strum_macros = { version = "0.25.3" }
syn = { version = "2.0.51" }
tempfile = { version = "3.9.0" }
test-case = { version = "3.3.1" }
thiserror = { version = "1.0.58" }
thiserror = { version = "1.0.57" }
tikv-jemallocator = { version = "0.5.0" }
toml = { version = "0.8.11" }
toml = { version = "0.8.9" }
tracing = { version = "0.1.40" }
tracing-indicatif = { version = "0.3.6" }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-tree = { version = "0.3.0" }
tracing-tree = { version = "0.2.4" }
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.42" }
wasm-bindgen-test = { version = "0.3.40" }
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 [800 built-in rules](https://docs.astral.sh/ruff/rules/), with native re-implementations
- 📏 Over [700 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.5
rev: v0.3.2
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 800 lint rules**, many of which are inspired by popular tools like Flake8,
**Ruff supports over 700 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,7 +429,6 @@ 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.5"
version = "0.3.2"
publish = false
authors = { workspace = true }
edition = { workspace = true }
@@ -41,7 +41,6 @@ is-macro = { workspace = true }
itertools = { workspace = true }
log = { workspace = true }
notify = { workspace = true }
num_cpus = { workspace = true }
path-absolutize = { workspace = true, features = ["once_cell_cache"] }
rayon = { workspace = true }
regex = { workspace = true }
@@ -54,7 +53,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 }
@@ -62,8 +61,9 @@ 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"] }
colored = { workspace = true, features = ["no-color"]}
insta = { workspace = true, features = ["filters", "json"] }
insta-cmd = { workspace = true }
tempfile = { workspace = true }

View File

@@ -496,7 +496,7 @@ pub struct FormatCommand {
pub range: Option<FormatRange>,
}
#[derive(Copy, Clone, Debug, clap::Parser)]
#[derive(Clone, Debug, clap::Parser)]
pub struct ServerCommand {
/// Enable preview mode; required for regular operation
#[arg(long)]

View File

@@ -252,7 +252,6 @@ mod test {
for file in [&pyproject_toml, &python_file, &notebook] {
fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.mode(0o000)
.open(file)?;

View File

@@ -1,5 +1,3 @@
use std::num::NonZeroUsize;
use crate::ExitStatus;
use anyhow::Result;
use ruff_linter::logging::LogLevel;
@@ -11,11 +9,7 @@ use tracing_subscriber::{
};
use tracing_tree::time::Uptime;
pub(crate) fn run_server(
preview: bool,
worker_threads: NonZeroUsize,
log_level: LogLevel,
) -> Result<ExitStatus> {
pub(crate) fn run_server(preview: bool, 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);
@@ -39,7 +33,7 @@ pub(crate) fn run_server(
tracing::subscriber::set_global_default(subscriber)?;
let server = Server::new(worker_threads)?;
let server = Server::new()?;
server.run().map(|()| ExitStatus::Success)
}

View File

@@ -2,7 +2,6 @@
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;
@@ -205,15 +204,10 @@ 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;
// 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,
)
commands::server::run_server(preview, log_level)
}
pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<ExitStatus> {

View File

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

View File

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

View File

@@ -1353,7 +1353,6 @@ 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}((-|,\s)\d{4})*
linter.flake8_copyright.notice_rgx = (?i)Copyright\s+((?:\(C\)|©)\s+)?\d{4}(-\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,6 +31,7 @@ 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 similarity index, but i'd like to keep this around
/// We currently prefer the 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,7 +1,6 @@
//! Generate Markdown documentation for applicable rules.
#![allow(clippy::print_stdout, clippy::print_stderr)]
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
@@ -98,13 +97,12 @@ 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!(
@@ -137,7 +135,6 @@ 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;
}
@@ -145,20 +142,6 @@ 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');
@@ -176,7 +159,7 @@ mod tests {
process_documentation(
"
See also [`lint.mccabe.max-complexity`] and [`lint.task-tags`].
Something [`else`][other]. Some [link](https://example.com).
Something [`else`][other].
## Options
@@ -191,7 +174,7 @@ Something [`else`][other]. Some [link](https://example.com).
output,
"
See also [`lint.mccabe.max-complexity`][lint.mccabe.max-complexity] and [`lint.task-tags`][lint.task-tags].
Something [`else`][other]. Some [link](https://example.com).
Something [`else`][other].
## Options

View File

@@ -180,22 +180,8 @@ 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,6 +25,11 @@ 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>`
@@ -45,6 +50,11 @@ 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;
@@ -61,6 +71,10 @@ 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,6 +24,7 @@ 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.5"
version = "0.3.2"
publish = false
authors = { workspace = true }
edition = { workspace = true }
@@ -15,6 +15,7 @@ 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"] }
@@ -74,9 +75,11 @@ 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,32 +36,3 @@ 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,7 +31,8 @@ def function(
kwonly_nonboolvalued_boolhint: bool = 1,
kwonly_nonboolvalued_boolstrhint: "bool" = 1,
**kw,
): ...
):
...
def used(do):
@@ -130,27 +131,4 @@ 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,69 +9,62 @@ 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
except (*a, *(RuntimeError, (KeyError, TypeError))): # error
pass
try:
pass
except (ValueError, *(RuntimeError, TypeError)): # ok
pass
try:
pass
except (ValueError, *[RuntimeError, *(TypeError,)]): # ok
pass
try:
pass
except* a + (RuntimeError, (KeyError, TypeError)): # Error
except (*a, *b): # ok
pass
try:
pass
except (ValueError, *(RuntimeError, TypeError)): # OK
pass
try:
pass
except (ValueError, *[RuntimeError, *(TypeError,)]): # OK
except (*a, *(RuntimeError, TypeError)): # ok
pass
try:
pass
except (*a, *b): # OK
except (*a, *(b, c)): # ok
pass
try:
pass
except (*a, *(RuntimeError, TypeError)): # OK
pass
try:
pass
except (*a, *(b, c)): # OK
pass
try:
pass
except (*a, *(*b, *c)): # OK
except (*a, *(*b, *c)): # ok
pass
@@ -81,52 +74,5 @@ def what_to_catch():
try:
pass
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
except what_to_catch(): # ok
pass

View File

@@ -174,49 +174,6 @@ 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,11 @@
# Cannot combine with C416. Should use list comprehension here.
even_nums = list(2 * x for x in range(3))
odd_nums = list(
2 * x + 1 for x in range(3)
)
# Short-circuit case, combine with C416 and should produce x = list(range(3))
x = list(x for x in range(3))
x = list(
x for x in range(3)
)
# Not built-in list.
def list(*args, **kwargs):
return None
list(2 * x for x in range(3))
list(x for x in range(3))

View File

@@ -1,30 +1,20 @@
# 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))}"
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")
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') }")
# 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))
# 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')} }")

View File

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

View File

@@ -33,9 +33,3 @@ 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

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

View File

@@ -227,11 +227,3 @@ class Repro[int](Protocol):
def impl(self) -> str:
"""Docstring"""
return self.func()
import typing
if typing.TYPE_CHECKING:
def contains_meaningful_ellipsis() -> list[int]:
"""Allow this in a TYPE_CHECKING block."""
...

View File

@@ -1,14 +0,0 @@
"""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

@@ -1,14 +0,0 @@
"""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

@@ -1,6 +0,0 @@
"""
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

@@ -1,6 +0,0 @@
"""
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,6 +79,5 @@ 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,8 +51,3 @@ 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

@@ -1,9 +0,0 @@
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

@@ -1,9 +0,0 @@
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

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

View File

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

View File

@@ -1,9 +0,0 @@
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

@@ -1,9 +0,0 @@
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

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

View File

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

View File

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

View File

@@ -2,6 +2,3 @@ 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

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

View File

@@ -84,22 +84,3 @@ 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,5 +3,3 @@ import ruff
import leading_prefix
import os
from . import leading_prefix
from .. import trailing_prefix
from ruff import check

View File

@@ -1,6 +0,0 @@
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,5 +104,3 @@ def func():
np.unicode_("asf")
np.who()
np.row_stack(([1,2], [3,4]))

View File

@@ -47,60 +47,4 @@ snapshot.file_uri[len(f's3://{self.s3_bucket_name}/'):]
{len(f's3://{self.s3_bucket_name}/'):1}
#: Okay
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
},
]
}
]
a = (1,

View File

@@ -1,228 +0,0 @@
{
"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,37 +87,6 @@
"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,8 +82,3 @@ 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

@@ -52,8 +52,3 @@ 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

@@ -1,8 +0,0 @@
"""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

@@ -1,9 +0,0 @@
"""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

@@ -1,23 +0,0 @@
"""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,7 +5,6 @@ class Person: # [eq-without-hash]
def __eq__(self, other):
return isinstance(other, Person) and other.name == self.name
# OK
class Language:
def __init__(self):
@@ -17,24 +16,8 @@ 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

@@ -11,13 +11,6 @@ def f():
print(X)
def f():
global X
if X > 0:
del X
###
# Non-errors.
###

View File

@@ -1,60 +0,0 @@
# 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

@@ -1,76 +0,0 @@
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

@@ -1,67 +0,0 @@
# 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

@@ -1,6 +0,0 @@
FIRST, FIRST = (1, 2) # PLW0128
FIRST, (FIRST, SECOND) = (1, (1, 2)) # PLW0128
FIRST, (FIRST, SECOND, (THIRD, FIRST)) = (1, (1, 2)) # PLW0128
FIRST, SECOND, THIRD, FIRST, SECOND = (1, 2, 3, 4) # PLW0128
FIRST, SECOND, _, _, _ignored = (1, 2, 3, 4, 5) # OK

View File

@@ -1,23 +0,0 @@
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 # [singledispatchmethod-function]
@staticmethod
def do(position):
pass

View File

@@ -1,28 +1,11 @@
def func():
import datetime
import datetime
import datetime as dt
from datetime import timezone
from datetime import timezone as tz
print(datetime.timezone(-1))
print(datetime.timezone(-1))
print(timezone.utc)
print(tz.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)
print(datetime.timezone.utc)
print(dt.timezone.utc)

View File

@@ -1,61 +0,0 @@
# 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

@@ -1,17 +0,0 @@
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,6 +1,9 @@
import math
# Errors
from math import e as special_e
from math import log as special_log
# Errors.
math.log(1, 2)
math.log(1, 10)
math.log(1, math.e)
@@ -8,10 +11,15 @@ 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)
@@ -32,7 +40,6 @@ 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

@@ -1,34 +0,0 @@
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

@@ -1,62 +0,0 @@
# 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,3 +1,4 @@
import typing
from typing import Annotated, Any, Literal, Optional, Tuple, Union, Hashable
@@ -25,6 +26,10 @@ def f(arg: str = None): # RUF013
pass
def f(arg: typing.List[str] = None): # RUF013
pass
def f(arg: Tuple[str] = None): # RUF013
pass
@@ -36,6 +41,10 @@ def f(arg: Optional[int] = None):
pass
def f(arg: typing.Optional[int] = None):
pass
# Union
@@ -51,6 +60,10 @@ 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
@@ -67,6 +80,10 @@ def f(arg: Union[int, str] = None): # RUF013
pass
def f(arg: typing.Union[int, str] = None): # RUF013
pass
# PEP 604 Union
@@ -113,6 +130,10 @@ 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): # RUF013
def f(arg: int = None): # RUF011
pass

View File

@@ -1,30 +0,0 @@
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,9 +100,3 @@ 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,10 +3,13 @@ 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:
@@ -17,10 +20,6 @@ def bad():
def bad():
import logging
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
@@ -51,10 +50,6 @@ def bad():
def good():
import logging
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
@@ -69,10 +64,6 @@ def good():
def fine():
import logging
logger = logging.getLogger(__name__)
try:
a = 1
except Exception:
@@ -80,20 +71,16 @@ 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())
def bad():
from logging import error, exception
from logging import error, exception
def bad():
try:
a = 1
except Exception:
@@ -104,8 +91,6 @@ def bad():
def good():
from logging import error, exception
try:
a = 1
except Exception:
@@ -113,8 +98,6 @@ def good():
def fine():
from logging import error, exception
try:
a = 1
except Exception:
@@ -122,9 +105,6 @@ def fine():
def fine():
from logging import error, exception
import sys
try:
a = 1
except Exception:
@@ -132,8 +112,6 @@ def fine():
def nested():
from logging import error, exception
try:
a = 1
except Exception:

View File

@@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_semantic::analyze::visibility;
use ruff_python_semantic::{Binding, BindingKind, Imported, ResolvedReference, ScopeKind};
use ruff_python_semantic::{Binding, BindingKind, Imported, ScopeKind};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
@@ -43,7 +43,6 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
Rule::UnusedStaticMethodArgument,
Rule::UnusedVariable,
Rule::SingledispatchMethod,
Rule::SingledispatchmethodFunction,
]) {
return;
}
@@ -92,29 +91,13 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
if checker.enabled(Rule::GlobalVariableNotAssigned) {
for (name, binding_id) in scope.bindings() {
let binding = checker.semantic.binding(binding_id);
// If the binding is a `global`, then it's a top-level `global` that was never
// assigned in the current scope. If it were assigned, the `global` would be
// shadowed by the assignment.
if binding.kind.is_global() {
// If the binding was conditionally deleted, it will include a reference within
// a `Del` context, but won't be shadowed by a `BindingKind::Deletion`, as in:
// ```python
// if condition:
// del var
// ```
if binding
.references
.iter()
.map(|id| checker.semantic.reference(*id))
.all(ResolvedReference::is_load)
{
diagnostics.push(Diagnostic::new(
pylint::rules::GlobalVariableNotAssigned {
name: (*name).to_string(),
},
binding.range(),
));
}
diagnostics.push(Diagnostic::new(
pylint::rules::GlobalVariableNotAssigned {
name: (*name).to_string(),
},
binding.range(),
));
}
}
}
@@ -420,10 +403,6 @@ 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::{all::DunderAllName, str::raw_contents_range};
use ruff_python_ast::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<DunderAllName>> = {
let exports: Option<Vec<&str>> = {
checker
.semantic
.global_scope()

View File

@@ -926,12 +926,6 @@ 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);
}
@@ -1289,9 +1283,6 @@ 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);
}

View File

@@ -30,7 +30,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}));
}
}
Stmt::Nonlocal(nonlocal @ ast::StmtNonlocal { names, range: _ }) => {
Stmt::Nonlocal(ast::StmtNonlocal { names, range: _ }) => {
if checker.enabled(Rule::AmbiguousVariableName) {
checker.diagnostics.extend(names.iter().filter_map(|name| {
pycodestyle::rules::ambiguous_variable_name(name, name.range())
@@ -50,9 +50,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
}
if checker.enabled(Rule::NonlocalAndGlobal) {
pylint::rules::nonlocal_and_global(checker, nonlocal);
}
}
Stmt::Break(_) => {
if checker.enabled(Rule::BreakOutsideLoop) {
@@ -1082,7 +1079,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_simplify::rules::if_with_same_arms(checker, if_);
}
if checker.enabled(Rule::NeedlessBool) {
flake8_simplify::rules::needless_bool(checker, stmt);
flake8_simplify::rules::needless_bool(checker, if_);
}
if checker.enabled(Rule::IfElseBlockInsteadOfDictLookup) {
flake8_simplify::rules::if_else_block_instead_of_dict_lookup(checker, if_);
@@ -1293,9 +1290,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::ManualDictComprehension) {
perflint::rules::manual_dict_comprehension(checker, target, body);
}
if checker.enabled(Rule::ModifiedIteratingSet) {
pylint::rules::modified_iterating_set(checker, for_stmt);
}
if checker.enabled(Rule::UnnecessaryListCast) {
perflint::rules::unnecessary_list_cast(checker, iter, body);
}
@@ -1318,9 +1312,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::TryExceptInLoop) {
perflint::rules::try_except_in_loop(checker, body);
}
if checker.enabled(Rule::ForLoopSetMutations) {
refurb::rules::for_loop_set_mutations(checker, for_stmt);
}
}
}
Stmt::Try(ast::StmtTry {
@@ -1398,9 +1389,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
Stmt::Assign(assign @ ast::StmtAssign { targets, value, .. }) => {
if checker.enabled(Rule::RedeclaredAssignedName) {
pylint::rules::redeclared_assigned_name(checker, targets);
}
if checker.enabled(Rule::LambdaAssignment) {
if let [target] = &targets[..] {
pycodestyle::rules::lambda_assignment(checker, target, value, None, stmt);
@@ -1509,9 +1497,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
}
if checker.enabled(Rule::ListReverseCopy) {
refurb::rules::list_assign_reversed(checker, assign);
}
}
Stmt::AnnAssign(
assign_stmt @ ast::StmtAnnAssign {

View File

@@ -2,7 +2,7 @@ use ruff_python_ast::StringLike;
use crate::checkers::ast::Checker;
use crate::codes::Rule;
use crate::rules::{flake8_bandit, flake8_pyi, flake8_quotes, ruff};
use crate::rules::{flake8_bandit, flake8_pyi, ruff};
/// Run lint rules over a [`StringLike`] syntax nodes.
pub(crate) fn string_like(string_like: StringLike, checker: &mut Checker) {
@@ -23,14 +23,4 @@ pub(crate) fn string_like(string_like: StringLike, checker: &mut Checker) {
flake8_pyi::rules::string_or_bytes_too_long(checker, string_like);
}
}
if checker.any_enabled(&[
Rule::BadQuotesInlineString,
Rule::BadQuotesMultilineString,
Rule::BadQuotesDocstring,
]) {
flake8_quotes::rules::check_string_quotes(checker, string_like);
}
if checker.enabled(Rule::UnnecessaryEscapedQuote) {
flake8_quotes::rules::unnecessary_escaped_quote(checker, string_like);
}
}

View File

@@ -31,9 +31,8 @@ use std::path::Path;
use itertools::Itertools;
use log::debug;
use ruff_python_ast::{
self as ast, all::DunderAllName, Comprehension, ElifElseClause, ExceptHandler, Expr,
ExprContext, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt,
Suite, UnaryOp,
self as ast, Comprehension, ElifElseClause, ExceptHandler, Expr, ExprContext, Keyword,
MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt, Suite, UnaryOp,
};
use ruff_text_size::{Ranged, TextRange, TextSize};
@@ -365,7 +364,6 @@ impl<'a> Visitor<'a> for Checker<'a> {
self.semantic.flags |= SemanticModelFlags::MODULE_DOCSTRING_BOUNDARY;
self.semantic.flags |= SemanticModelFlags::FUTURES_BOUNDARY;
if !(self.semantic.seen_import_boundary()
|| stmt.is_ipy_escape_command_stmt()
|| helpers::is_assignment_to_a_dunder(stmt)
|| helpers::in_nested_block(self.semantic.current_statements())
|| imports::is_matplotlib_activation(stmt, self.semantic())
@@ -542,11 +540,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
for name in names {
if let Some((scope_id, binding_id)) = self.semantic.nonlocal(name) {
// Mark the binding as "used".
self.semantic.add_local_reference(
binding_id,
ExprContext::Load,
name.range(),
);
self.semantic.add_local_reference(binding_id, name.range());
// Mark the binding in the enclosing scope as "rebound" in the current
// scope.
@@ -939,7 +933,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
&& !self.semantic.in_deferred_type_definition()
&& self.semantic.in_type_definition()
&& self.semantic.future_annotations()
&& (self.semantic.in_annotation() || self.source_type.is_stub())
&& (self.semantic.in_typing_only_annotation() || self.source_type.is_stub())
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
self.visit.string_type_definitions.push((
@@ -1838,7 +1832,7 @@ impl<'a> Checker<'a> {
if matches!(
parent,
Stmt::AnnAssign(ast::StmtAnnAssign { value: None, .. })
) && !self.semantic.in_annotation()
) && !(self.semantic.in_annotation() || self.source_type.is_stub())
{
self.add_binding(id, expr.range(), BindingKind::Annotation, flags);
return;
@@ -2100,32 +2094,32 @@ impl<'a> Checker<'a> {
fn visit_exports(&mut self) {
let snapshot = self.semantic.snapshot();
let exports: Vec<DunderAllName> = self
let exports: Vec<(&str, TextRange)> = self
.semantic
.global_scope()
.get_all("__all__")
.map(|binding_id| &self.semantic.bindings[binding_id])
.filter_map(|binding| match &binding.kind {
BindingKind::Export(Export { names }) => Some(names.iter().copied()),
BindingKind::Export(Export { names }) => {
Some(names.iter().map(|name| (*name, binding.range())))
}
_ => None,
})
.flatten()
.collect();
for export in exports {
let (name, range) = (export.name(), export.range());
for (name, range) in exports {
if let Some(binding_id) = self.semantic.global_scope().get(name) {
self.semantic.flags |= SemanticModelFlags::DUNDER_ALL_DEFINITION;
// Mark anything referenced in `__all__` as used.
self.semantic
.add_global_reference(binding_id, ExprContext::Load, range);
self.semantic.flags -= SemanticModelFlags::DUNDER_ALL_DEFINITION;
// TODO(charlie): `range` here should be the range of the name in `__all__`, not
// the range of `__all__` itself.
self.semantic.add_global_reference(binding_id, range);
} else {
if self.semantic.global_scope().uses_star_imports() {
if self.enabled(Rule::UndefinedLocalWithImportStarUsage) {
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::UndefinedLocalWithImportStarUsage {
name: name.to_string(),
name: (*name).to_string(),
},
range,
));
@@ -2135,7 +2129,7 @@ impl<'a> Checker<'a> {
if !self.path.ends_with("__init__.py") {
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::UndefinedExport {
name: name.to_string(),
name: (*name).to_string(),
},
range,
));

View File

@@ -20,7 +20,12 @@ use crate::rules::isort::block::{Block, BlockBuilder};
use crate::settings::LinterSettings;
fn extract_import_map(path: &Path, package: Option<&Path>, blocks: &[&Block]) -> Option<ImportMap> {
let module_path = to_module_path(package?, path)?;
let Some(package) = package else {
return None;
};
let Some(module_path) = to_module_path(package, path) else {
return None;
};
let num_imports = blocks.iter().map(|block| block.imports.len()).sum();
let mut module_imports = Vec::with_capacity(num_imports);

View File

@@ -41,7 +41,7 @@ pub(crate) fn check_tokens(
Rule::BlankLinesAfterFunctionOrClass,
Rule::BlankLinesBeforeNestedDefinition,
]) {
BlankLinesChecker::new(locator, stylist, settings, source_type, cell_offsets)
BlankLinesChecker::new(locator, stylist, settings, source_type)
.check_lines(tokens, &mut diagnostics);
}
@@ -126,6 +126,18 @@ pub(crate) fn check_tokens(
flake8_quotes::rules::avoidable_escaped_quote(&mut diagnostics, tokens, locator, settings);
}
if settings.rules.enabled(Rule::UnnecessaryEscapedQuote) {
flake8_quotes::rules::unnecessary_escaped_quote(&mut diagnostics, tokens, locator);
}
if settings.rules.any_enabled(&[
Rule::BadQuotesInlineString,
Rule::BadQuotesMultilineString,
Rule::BadQuotesDocstring,
]) {
flake8_quotes::rules::check_string_quotes(&mut diagnostics, tokens, locator, settings);
}
if settings.rules.any_enabled(&[
Rule::SingleLineImplicitStringConcatenation,
Rule::MultiLineImplicitStringConcatenation,

View File

@@ -234,7 +234,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "C3002") => (RuleGroup::Stable, rules::pylint::rules::UnnecessaryDirectLambdaCall),
(Pylint, "E0100") => (RuleGroup::Stable, rules::pylint::rules::YieldInInit),
(Pylint, "E0101") => (RuleGroup::Stable, rules::pylint::rules::ReturnInInit),
(Pylint, "E0115") => (RuleGroup::Preview, rules::pylint::rules::NonlocalAndGlobal),
(Pylint, "E0116") => (RuleGroup::Stable, rules::pylint::rules::ContinueInFinally),
(Pylint, "E0117") => (RuleGroup::Stable, rules::pylint::rules::NonlocalWithoutBinding),
(Pylint, "E0118") => (RuleGroup::Stable, rules::pylint::rules::LoadBeforeGlobalDeclaration),
@@ -257,7 +256,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "E1310") => (RuleGroup::Stable, rules::pylint::rules::BadStrStripCall),
(Pylint, "E1507") => (RuleGroup::Stable, rules::pylint::rules::InvalidEnvvarValue),
(Pylint, "E1519") => (RuleGroup::Preview, rules::pylint::rules::SingledispatchMethod),
(Pylint, "E1520") => (RuleGroup::Preview, rules::pylint::rules::SingledispatchmethodFunction),
(Pylint, "E1700") => (RuleGroup::Stable, rules::pylint::rules::YieldFromInAsyncFunction),
(Pylint, "E2502") => (RuleGroup::Stable, rules::pylint::rules::BidirectionalUnicode),
(Pylint, "E2510") => (RuleGroup::Stable, rules::pylint::rules::InvalidCharacterBackspace),
@@ -265,7 +263,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "E2513") => (RuleGroup::Stable, rules::pylint::rules::InvalidCharacterEsc),
(Pylint, "E2514") => (RuleGroup::Stable, rules::pylint::rules::InvalidCharacterNul),
(Pylint, "E2515") => (RuleGroup::Stable, rules::pylint::rules::InvalidCharacterZeroWidthSpace),
(Pylint, "E4703") => (RuleGroup::Preview, rules::pylint::rules::ModifiedIteratingSet),
(Pylint, "R0124") => (RuleGroup::Stable, rules::pylint::rules::ComparisonWithItself),
(Pylint, "R0133") => (RuleGroup::Stable, rules::pylint::rules::ComparisonOfConstant),
(Pylint, "R0202") => (RuleGroup::Preview, rules::pylint::rules::NoClassmethodDecorator),
@@ -295,10 +292,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
#[allow(deprecated)]
(Pylint, "R6301") => (RuleGroup::Nursery, rules::pylint::rules::NoSelfUse),
(Pylint, "W0108") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryLambda),
(Pylint, "W0117") => (RuleGroup::Preview, rules::pylint::rules::NanComparison),
(Pylint, "W0120") => (RuleGroup::Stable, rules::pylint::rules::UselessElseOnLoop),
(Pylint, "W0127") => (RuleGroup::Stable, rules::pylint::rules::SelfAssigningVariable),
(Pylint, "W0128") => (RuleGroup::Preview, rules::pylint::rules::RedeclaredAssignedName),
(Pylint, "W0129") => (RuleGroup::Stable, rules::pylint::rules::AssertOnStringLiteral),
(Pylint, "W0131") => (RuleGroup::Stable, rules::pylint::rules::NamedExprWithoutContext),
(Pylint, "W0133") => (RuleGroup::Preview, rules::pylint::rules::UselessExceptionStatement),
@@ -1045,14 +1040,11 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Refurb, "132") => (RuleGroup::Nursery, rules::refurb::rules::CheckAndRemoveFromSet),
(Refurb, "136") => (RuleGroup::Preview, rules::refurb::rules::IfExprMinMax),
(Refurb, "140") => (RuleGroup::Preview, rules::refurb::rules::ReimplementedStarmap),
(Refurb, "142") => (RuleGroup::Preview, rules::refurb::rules::ForLoopSetMutations),
(Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy),
(Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate),
(Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant),
(Refurb, "157") => (RuleGroup::Preview, rules::refurb::rules::VerboseDecimalConstructor),
(Refurb, "161") => (RuleGroup::Preview, rules::refurb::rules::BitCount),
(Refurb, "163") => (RuleGroup::Preview, rules::refurb::rules::RedundantLogBase),
(Refurb, "164") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryFromFloat),
(Refurb, "167") => (RuleGroup::Preview, rules::refurb::rules::RegexFlagAlias),
(Refurb, "168") => (RuleGroup::Preview, rules::refurb::rules::IsinstanceTypeNone),
(Refurb, "169") => (RuleGroup::Preview, rules::refurb::rules::TypeNoneComparison),
@@ -1060,7 +1052,6 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Refurb, "177") => (RuleGroup::Preview, rules::refurb::rules::ImplicitCwd),
(Refurb, "180") => (RuleGroup::Preview, rules::refurb::rules::MetaClassABCMeta),
(Refurb, "181") => (RuleGroup::Preview, rules::refurb::rules::HashlibDigestHex),
(Refurb, "187") => (RuleGroup::Preview, rules::refurb::rules::ListReverseCopy),
// flake8-logging
(Flake8Logging, "001") => (RuleGroup::Stable, rules::flake8_logging::rules::DirectLoggerInstantiation),

View File

@@ -40,7 +40,10 @@ pub(crate) fn delete_stmt(
locator: &Locator,
indexer: &Indexer,
) -> Edit {
if parent.is_some_and(|parent| is_lone_child(stmt, parent)) {
if parent
.map(|parent| is_lone_child(stmt, parent))
.unwrap_or_default()
{
// If removing this node would lead to an invalid syntax tree, replace
// it with a `pass`.
Edit::range_replacement("pass".to_string(), stmt.range())
@@ -83,7 +86,6 @@ pub(crate) fn delete_comment(range: TextRange, locator: &Locator) -> Edit {
}
// Ex) `x = 1 # noqa`
else if range.end() + trailing_space_len == line_range.end() {
// Replace `x = 1 # noqa` with `x = 1`.
Edit::deletion(range.start() - leading_space_len, line_range.end())
}
// Ex) `x = 1 # noqa # type: ignore`
@@ -94,15 +96,13 @@ pub(crate) fn delete_comment(range: TextRange, locator: &Locator) -> Edit {
))
.starts_with('#')
{
// Replace `# noqa # type: ignore` with `# type: ignore`.
Edit::deletion(range.start(), range.end() + trailing_space_len)
}
// Ex) `x = 1 # noqa here`
else {
// Replace `# noqa here` with `# here`.
Edit::range_replacement(
"# ".to_string(),
TextRange::new(range.start(), range.end() + trailing_space_len),
Edit::deletion(
range.start() + "# ".text_len(),
range.end() + trailing_space_len,
)
}
}

View File

@@ -278,7 +278,9 @@ impl<'a> Insertion<'a> {
/// Find the end of the last docstring.
fn match_docstring_end(body: &[Stmt]) -> Option<TextSize> {
let mut iter = body.iter();
let mut stmt = iter.next()?;
let Some(mut stmt) = iter.next() else {
return None;
};
if !is_docstring_stmt(stmt) {
return None;
}

View File

@@ -26,14 +26,17 @@ use std::path::{Path, PathBuf};
/// Return `true` if the directory at the given `Path` appears to be a Python
/// package.
pub fn is_package(path: &Path, namespace_packages: &[PathBuf]) -> bool {
namespace_packages
.iter()
.any(|namespace_package| path.starts_with(namespace_package))
|| path.join("__init__.py").is_file()
path.join("__init__.py").is_file()
|| namespace_packages
.iter()
.any(|namespace_package| namespace_package == path)
}
/// Return the package root for the given path to a directory with Python file.
pub fn detect_package_root<'a>(path: &'a Path, namespace_packages: &[PathBuf]) -> Option<&'a Path> {
/// Return the package root for the given Python file.
pub fn detect_package_root<'a>(
path: &'a Path,
namespace_packages: &'a [PathBuf],
) -> Option<&'a Path> {
let mut current = None;
for parent in path.ancestors() {
if !is_package(parent, namespace_packages) {
@@ -81,39 +84,4 @@ mod tests {
None,
);
}
#[test]
fn package_detection_with_namespace_packages() {
assert_eq!(
detect_package_root(&test_resource_path("project/python_modules/core/core"), &[],),
Some(test_resource_path("project/python_modules/core/core").as_path())
);
assert_eq!(
detect_package_root(
&test_resource_path("project/python_modules/core/core"),
&[test_resource_path("project/python_modules/core"),],
),
Some(test_resource_path("project/python_modules/core").as_path())
);
assert_eq!(
detect_package_root(
&test_resource_path("project/python_modules/core/core"),
&[
test_resource_path("project/python_modules/core"),
test_resource_path("project/python_modules"),
],
),
Some(test_resource_path("project/python_modules").as_path())
);
assert_eq!(
detect_package_root(
&test_resource_path("project/python_modules/core/core"),
&[test_resource_path("project/python_modules"),],
),
Some(test_resource_path("project/python_modules").as_path())
);
}
}

View File

@@ -115,9 +115,6 @@ pub enum Linter {
/// [flake8-import-conventions](https://github.com/joaopalmeiro/flake8-import-conventions)
#[prefix = "ICN"]
Flake8ImportConventions,
/// [flake8-logging](https://pypi.org/project/flake8-logging/)
#[prefix = "LOG"]
Flake8Logging,
/// [flake8-logging-format](https://pypi.org/project/flake8-logging-format/)
#[prefix = "G"]
Flake8LoggingFormat,
@@ -205,6 +202,9 @@ pub enum Linter {
/// [refurb](https://pypi.org/project/refurb/)
#[prefix = "FURB"]
Refurb,
/// [flake8-logging](https://pypi.org/project/flake8-logging/)
#[prefix = "LOG"]
Flake8Logging,
/// Ruff-specific rules
#[prefix = "RUF"]
Ruff,
@@ -257,6 +257,9 @@ impl Rule {
| Rule::TrailingWhitespace => LintSource::PhysicalLines,
Rule::AmbiguousUnicodeCharacterComment
| Rule::AvoidableEscapedQuote
| Rule::BadQuotesDocstring
| Rule::BadQuotesInlineString
| Rule::BadQuotesMultilineString
| Rule::BlanketNOQA
| Rule::BlanketTypeIgnore
| Rule::BlankLineAfterDecorator

View File

@@ -4,9 +4,8 @@ use anyhow::{anyhow, Result};
use itertools::Itertools;
use ruff_diagnostics::Edit;
use ruff_python_codegen::Stylist;
use ruff_python_semantic::{Binding, BindingKind, Scope, ScopeId, SemanticModel};
use ruff_text_size::{Ranged, TextSize};
use ruff_text_size::Ranged;
pub(crate) struct Renamer;
@@ -105,7 +104,6 @@ impl Renamer {
target: &str,
scope: &Scope,
semantic: &SemanticModel,
stylist: &Stylist,
) -> Result<(Edit, Vec<Edit>)> {
let mut edits = vec![];
@@ -132,9 +130,7 @@ impl Renamer {
});
let scope = scope_id.map_or(scope, |scope_id| &semantic.scopes[scope_id]);
edits.extend(Renamer::rename_in_scope(
name, target, scope, semantic, stylist,
));
edits.extend(Renamer::rename_in_scope(name, target, scope, semantic));
// Find any scopes in which the symbol is referenced as `nonlocal` or `global`. For example,
// given:
@@ -164,9 +160,7 @@ impl Renamer {
.copied()
{
let scope = &semantic.scopes[scope_id];
edits.extend(Renamer::rename_in_scope(
name, target, scope, semantic, stylist,
));
edits.extend(Renamer::rename_in_scope(name, target, scope, semantic));
}
// Deduplicate any edits.
@@ -186,7 +180,6 @@ impl Renamer {
target: &str,
scope: &Scope,
semantic: &SemanticModel,
stylist: &Stylist,
) -> Vec<Edit> {
let mut edits = vec![];
@@ -209,17 +202,7 @@ impl Renamer {
// Rename the references to the binding.
edits.extend(binding.references().map(|reference_id| {
let reference = semantic.reference(reference_id);
let replacement = {
if reference.in_dunder_all_definition() {
debug_assert!(!reference.range().is_empty());
let quote = stylist.quote();
format!("{quote}{target}{quote}")
} else {
debug_assert_eq!(TextSize::of(name), reference.range().len());
target.to_string()
}
};
Edit::range_replacement(replacement, reference.range())
Edit::range_replacement(target.to_string(), reference.range())
}));
}
}
@@ -272,7 +255,6 @@ impl Renamer {
| BindingKind::ClassDefinition(_)
| BindingKind::FunctionDefinition(_)
| BindingKind::Deletion
| BindingKind::ConditionalDeletion(_)
| BindingKind::UnboundException(_) => {
Some(Edit::range_replacement(target.to_string(), binding.range()))
}

View File

@@ -43,49 +43,7 @@ impl Violation for CommentedOutCode {
}
}
/// ERA001
pub(crate) fn commented_out_code(
diagnostics: &mut Vec<Diagnostic>,
locator: &Locator,
indexer: &Indexer,
settings: &LinterSettings,
) {
// Skip comments within `/// script` tags.
let mut in_script_tag = false;
// Iterate over all comments in the document.
for range in indexer.comment_ranges() {
let line = locator.lines(*range);
// Detect `/// script` tags.
if in_script_tag {
if is_script_tag_end(line) {
in_script_tag = false;
}
} else {
if is_script_tag_start(line) {
in_script_tag = true;
}
}
// Skip comments within `/// script` tags.
if in_script_tag {
continue;
}
// Verify that the comment is on its own line, and that it contains code.
if is_own_line_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {
let mut diagnostic = Diagnostic::new(CommentedOutCode, *range);
diagnostic.set_fix(Fix::display_only_edit(Edit::range_deletion(
locator.full_lines_range(*range),
)));
diagnostics.push(diagnostic);
}
}
}
/// Returns `true` if line contains an own-line comment.
fn is_own_line_comment(line: &str) -> bool {
fn is_standalone_comment(line: &str) -> bool {
for char in line.chars() {
if char == '#' {
return true;
@@ -97,16 +55,23 @@ fn is_own_line_comment(line: &str) -> bool {
unreachable!("Comment should contain '#' character")
}
/// Returns `true` if the line appears to start a script tag.
///
/// See: <https://peps.python.org/pep-0723/>
fn is_script_tag_start(line: &str) -> bool {
line == "# /// script"
}
/// ERA001
pub(crate) fn commented_out_code(
diagnostics: &mut Vec<Diagnostic>,
locator: &Locator,
indexer: &Indexer,
settings: &LinterSettings,
) {
for range in indexer.comment_ranges() {
let line = locator.full_lines(*range);
/// Returns `true` if the line appears to start a script tag.
///
/// See: <https://peps.python.org/pep-0723/>
fn is_script_tag_end(line: &str) -> bool {
line == "# ///"
// Verify that the comment is on its own line, and that it contains code.
if is_standalone_comment(line) && comment_contains_code(line, &settings.task_tags[..]) {
let mut diagnostic = Diagnostic::new(CommentedOutCode, *range);
diagnostic.set_fix(Fix::display_only_edit(Edit::range_deletion(
locator.full_lines_range(*range),
)));
diagnostics.push(diagnostic);
}
}
}

View File

@@ -245,7 +245,6 @@ ERA001.py:36:1: ERA001 Found commented-out code
36 |-# except:
37 36 | # except Foo:
38 37 | # except Exception as e: print(e)
39 38 |
ERA001.py:37:1: ERA001 Found commented-out code
|
@@ -263,8 +262,6 @@ ERA001.py:37:1: ERA001 Found commented-out code
36 36 | # except:
37 |-# except Foo:
38 37 | # except Exception as e: print(e)
39 38 |
40 39 |
ERA001.py:38:1: ERA001 Found commented-out code
|
@@ -280,44 +277,3 @@ ERA001.py:38:1: ERA001 Found commented-out code
36 36 | # except:
37 37 | # except Foo:
38 |-# except Exception as e: print(e)
39 38 |
40 39 |
41 40 | # Script tag without an opening tag (Error)
ERA001.py:44:1: ERA001 Found commented-out code
|
43 | # requires-python = ">=3.11"
44 | # dependencies = [
| ^^^^^^^^^^^^^^^^^^ ERA001
45 | # "requests<3",
46 | # "rich",
|
= help: Remove commented-out code
Display-only fix
41 41 | # Script tag without an opening tag (Error)
42 42 |
43 43 | # requires-python = ">=3.11"
44 |-# dependencies = [
45 44 | # "requests<3",
46 45 | # "rich",
47 46 | # ]
ERA001.py:47:1: ERA001 Found commented-out code
|
45 | # "requests<3",
46 | # "rich",
47 | # ]
| ^^^ ERA001
48 | # ///
|
= help: Remove commented-out code
Display-only fix
44 44 | # dependencies = [
45 45 | # "requests<3",
46 46 | # "rich",
47 |-# ]
48 47 | # ///
49 48 |
50 49 | # Script tag (OK)

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