Compare commits
10 Commits
cjm/colors
...
dhruv/toke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b47794278 | ||
|
|
e9d5ca2fe1 | ||
|
|
07057c6a35 | ||
|
|
3af851109f | ||
|
|
c0c065f1ca | ||
|
|
e837888c37 | ||
|
|
79861da8f6 | ||
|
|
035ac75fae | ||
|
|
b5cc384bb1 | ||
|
|
78ee6441a7 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -2,8 +2,6 @@
|
||||
|
||||
crates/ruff_linter/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
|
||||
crates/ruff_linter/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
|
||||
crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_2.py text eol=crlf
|
||||
crates/ruff_linter/resources/test/fixtures/pycodestyle/W391_3.py text eol=crlf
|
||||
|
||||
crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring_code_examples_crlf.py text eol=crlf
|
||||
crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap text eol=crlf
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -3,8 +3,6 @@ Thank you for taking the time to report an issue! We're glad to have you involve
|
||||
|
||||
If you're filing a bug report, please consider including the following information:
|
||||
|
||||
* List of keywords you searched for before creating this issue. Write them down here so that others can find this issue more easily and help provide feedback.
|
||||
e.g. "RUF001", "unused variable", "Jupyter notebook"
|
||||
* A minimal code snippet that reproduces the bug.
|
||||
* The command you invoked (e.g., `ruff /path/to/file.py --fix`), ideally including the `--isolated` flag.
|
||||
* The current Ruff settings (any relevant sections from your `pyproject.toml`).
|
||||
|
||||
21
.github/dependabot.yml
vendored
Normal file
21
.github/dependabot.yml
vendored
Normal 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"]
|
||||
60
.github/renovate.json5
vendored
60
.github/renovate.json5
vendored
@@ -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"],
|
||||
},
|
||||
}
|
||||
19
.github/workflows/ci.yaml
vendored
19
.github/workflows/ci.yaml
vendored
@@ -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: |
|
||||
@@ -525,23 +525,8 @@ jobs:
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Codspeed comes with a very ancient cargo version (1.66) that resolves features flags differently than what we use now.
|
||||
# This can result in build failures; see https://github.com/astral-sh/ruff/pull/10700.
|
||||
# There's a pending codspeed PR to upgrade to a newer cargo version, but until that's merged, we need to use the workaround below.
|
||||
# https://github.com/CodSpeedHQ/codspeed-rust/pull/31
|
||||
# What we do is to call cargo build manually with the correct feature flags and RUSTC settings. We'll have to
|
||||
# manually maintain the list of benchmarks to run with codspeed (the benefit is that we could detect which benchmarks to run and build based on the changes).
|
||||
# This is inspired by https://github.com/oxc-project/oxc/blob/a0532adc654039a0c7ead7b35216dfa0b0cb8e8f/.github/workflows/benchmark.yml
|
||||
- name: "Build benchmarks"
|
||||
env:
|
||||
RUSTFLAGS: "-C debuginfo=2 -C strip=none -g --cfg codspeed"
|
||||
shell: bash
|
||||
# Build all benchmarks, copy the binary to the codspeed directory, remove any `*.d` files that might have been created.
|
||||
run: |
|
||||
cargo build --release -p ruff_benchmark --bench parser --bench linter --bench formatter --bench lexer --features=codspeed
|
||||
mkdir -p ./target/codspeed/ruff_benchmark
|
||||
cp ./target/release/deps/{lexer,parser,linter,formatter}* target/codspeed/ruff_benchmark/
|
||||
rm -rf ./target/codspeed/ruff_benchmark/*.d
|
||||
run: cargo codspeed build --features codspeed -p ruff_benchmark
|
||||
|
||||
- name: "Run benchmarks"
|
||||
uses: CodSpeedHQ/action@v2
|
||||
|
||||
11
.github/workflows/release.yaml
vendored
11
.github/workflows/release.yaml
vendored
@@ -28,6 +28,7 @@ env:
|
||||
CARGO_NET_RETRY: 10
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTUP_MAX_RETRIES: 10
|
||||
MATURIN_VERSION: "1.4.0"
|
||||
|
||||
jobs:
|
||||
sdist:
|
||||
@@ -44,6 +45,7 @@ jobs:
|
||||
- name: "Build sdist"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
command: sdist
|
||||
args: --out dist
|
||||
- name: "Test sdist"
|
||||
@@ -72,6 +74,7 @@ jobs:
|
||||
- name: "Build wheels - x86_64"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
target: x86_64
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel - x86_64"
|
||||
@@ -112,6 +115,7 @@ jobs:
|
||||
- name: "Build wheels - universal2"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
args: --release --locked --target universal2-apple-darwin --out dist
|
||||
- name: "Test wheel - universal2"
|
||||
run: |
|
||||
@@ -160,6 +164,7 @@ jobs:
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --locked --out dist
|
||||
- name: "Test wheel"
|
||||
@@ -208,6 +213,7 @@ jobs:
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: auto
|
||||
args: --release --locked --out dist
|
||||
@@ -270,6 +276,7 @@ jobs:
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: auto
|
||||
docker-options: ${{ matrix.platform.maturin_docker_options }}
|
||||
@@ -326,6 +333,7 @@ jobs:
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
target: ${{ matrix.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --locked --out dist
|
||||
@@ -381,6 +389,7 @@ jobs:
|
||||
- name: "Build wheels"
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
maturin-version: ${{ env.MATURIN_VERSION }}
|
||||
target: ${{ matrix.platform.target }}
|
||||
manylinux: musllinux_1_2
|
||||
args: --release --locked --out dist
|
||||
@@ -517,7 +526,7 @@ jobs:
|
||||
path: binaries
|
||||
merge-multiple: true
|
||||
- name: "Publish to GitHub"
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: binaries/*
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -92,7 +92,6 @@ coverage.xml
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
repos/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
158
CHANGELOG.md
158
CHANGELOG.md
@@ -1,154 +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
|
||||
|
||||
- Improve single-`with` item formatting for Python 3.8 or older ([#10276](https://github.com/astral-sh/ruff/pull/10276))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`pyupgrade`\] Allow fixes for f-string rule regardless of line length (`UP032`) ([#10263](https://github.com/astral-sh/ruff/pull/10263))
|
||||
- \[`pycodestyle`\] Include actual conditions in E712 diagnostics ([#10254](https://github.com/astral-sh/ruff/pull/10254))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix trailing kwargs end of line comment after slash ([#10297](https://github.com/astral-sh/ruff/pull/10297))
|
||||
- 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))
|
||||
- Treat `typing.Annotated` subscripts as type definitions ([#10285](https://github.com/astral-sh/ruff/pull/10285))
|
||||
|
||||
## 0.3.1
|
||||
|
||||
### Preview features
|
||||
@@ -254,7 +105,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 +754,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 +883,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 +1051,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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -123,8 +125,8 @@ prior to merging.
|
||||
Ruff is structured as a monorepo with a [flat crate structure](https://matklad.github.io/2021/08/22/large-rust-workspaces.html),
|
||||
such that all crates are contained in a flat `crates` directory.
|
||||
|
||||
The vast majority of the code, including all lint rules, lives in the `ruff_linter` crate (located
|
||||
at `crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
|
||||
The vast majority of the code, including all lint rules, lives in the `ruff` crate (located at
|
||||
`crates/ruff_linter`). As a contributor, that's the crate that'll be most relevant to you.
|
||||
|
||||
At the time of writing, the repository includes the following crates:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -330,13 +329,13 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
|
||||
|
||||
### Creating a new release
|
||||
|
||||
1. Install `uv`: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
||||
1. Run `./scripts/release/bump.sh`; this command will:
|
||||
- Generate a temporary virtual environment with `rooster`
|
||||
We use an experimental in-house tool for managing releases.
|
||||
|
||||
1. Install `rooster`: `pip install git+https://github.com/zanieb/rooster@main`
|
||||
1. Run `rooster release`; this command will:
|
||||
- Generate a changelog entry in `CHANGELOG.md`
|
||||
- Update versions in `pyproject.toml` and `Cargo.toml`
|
||||
- Update references to versions in the `README.md` and documentation
|
||||
- Display contributors for the release
|
||||
1. The changelog should then be editorialized for consistency
|
||||
- Often labels will be missing from pull requests they will need to be manually organized into the proper section
|
||||
- Changes should be edited to be user-facing descriptions, avoiding internal details
|
||||
@@ -360,7 +359,7 @@ even patch releases may contain [non-backwards-compatible changes](https://semve
|
||||
1. Open the draft release in the GitHub release section
|
||||
1. Copy the changelog for the release into the GitHub release
|
||||
- See previous releases for formatting of section headers
|
||||
1. Append the contributors from the `bump.sh` script
|
||||
1. Generate the contributor list with `rooster contributors` and add to the release notes
|
||||
1. If needed, [update the schemastore](https://github.com/astral-sh/ruff/blob/main/scripts/update_schemastore.py).
|
||||
1. One can determine if an update is needed when
|
||||
`git diff old-version-tag new-version-tag -- ruff.schema.json` returns a non-empty diff.
|
||||
@@ -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:
|
||||
|
||||
543
Cargo.lock
generated
543
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
51
Cargo.toml
51
Cargo.toml
@@ -12,28 +12,29 @@ 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"] }
|
||||
anstream = { version = "0.6.13" }
|
||||
anyhow = { version = "1.0.80" }
|
||||
argfile = { version = "0.1.6" }
|
||||
assert_cmd = { version = "2.0.13" }
|
||||
bincode = { version = "1.3.3" }
|
||||
bitflags = { version = "2.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"] }
|
||||
chrono = { version = "0.4.34", default-features = false, features = ["clock"] }
|
||||
clap = { version = "4.5.1", 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" }
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
crossbeam = { version = "0.8.4" }
|
||||
dirs = { version = "5.0.0" }
|
||||
drop_bomb = { version = "0.1.5" }
|
||||
env_logger = { version = "0.11.0" }
|
||||
env_logger = { version = "0.10.1" }
|
||||
fern = { version = "0.6.1" }
|
||||
filetime = { version = "0.2.23" }
|
||||
fs-err = { version = "2.11.0" }
|
||||
@@ -46,36 +47,30 @@ 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" }
|
||||
js-sys = { version = "0.3.69" }
|
||||
jod-thread = { version = "0.1.2" }
|
||||
js-sys = { version = "0.3.67" }
|
||||
lalrpop-util = { version = "0.20.0", default-features = false }
|
||||
lexical-parse-float = { version = "0.8.0", features = ["format"] }
|
||||
libc = { version = "0.2.153" }
|
||||
libcst = { version = "1.1.0", default-features = false }
|
||||
log = { version = "0.4.17" }
|
||||
lsp-server = { version = "0.7.6" }
|
||||
lsp-types = { version = "0.95.0", features = ["proposed"] }
|
||||
memchr = { version = "2.7.1" }
|
||||
mimalloc = { version = "0.1.39" }
|
||||
natord = { version = "1.0.9" }
|
||||
notify = { version = "6.1.1" }
|
||||
num_cpus = { version = "1.16.0" }
|
||||
once_cell = { version = "1.19.0" }
|
||||
owo-colors = { version = "4.0.0" }
|
||||
path-absolutize = { version = "3.1.1" }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { version = "0.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" }
|
||||
@@ -89,32 +84,30 @@ 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" }
|
||||
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" }
|
||||
unicode_names2 = { version = "1.2.1" }
|
||||
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 = { version = "0.2.84" }
|
||||
wasm-bindgen-test = { version = "0.3.40" }
|
||||
wild = { version = "2" }
|
||||
|
||||
[workspace.lints.rust]
|
||||
|
||||
33
README.md
33
README.md
@@ -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)
|
||||
@@ -129,7 +129,7 @@ and with [a variety of other package managers](https://docs.astral.sh/ruff/insta
|
||||
To run Ruff as a linter, try any of the following:
|
||||
|
||||
```shell
|
||||
ruff check # Lint all files in the current directory (and any subdirectories).
|
||||
ruff check . # Lint all files in the current directory (and any subdirectories).
|
||||
ruff check path/to/code/ # Lint all files in `/path/to/code` (and any subdirectories).
|
||||
ruff check path/to/code/*.py # Lint all `.py` files in `/path/to/code`.
|
||||
ruff check path/to/code/to/file.py # Lint `file.py`.
|
||||
@@ -139,7 +139,7 @@ ruff check @arguments.txt # Lint using an input file, treating its con
|
||||
Or, to run Ruff as a formatter:
|
||||
|
||||
```shell
|
||||
ruff format # Format all files in the current directory (and any subdirectories).
|
||||
ruff format . # Format all files in the current directory (and any subdirectories).
|
||||
ruff format path/to/code/ # Format all files in `/path/to/code` (and any subdirectories).
|
||||
ruff format path/to/code/*.py # Format all `.py` files in `/path/to/code`.
|
||||
ruff format path/to/code/to/file.py # Format `file.py`.
|
||||
@@ -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.1
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -183,9 +183,10 @@ Ruff can be configured through a `pyproject.toml`, `ruff.toml`, or `.ruff.toml`
|
||||
[_Configuration_](https://docs.astral.sh/ruff/configuration/), or [_Settings_](https://docs.astral.sh/ruff/settings/)
|
||||
for a complete list of all configuration options).
|
||||
|
||||
If left unspecified, Ruff's default configuration is equivalent to the following `ruff.toml` file:
|
||||
If left unspecified, Ruff's default configuration is equivalent to:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = [
|
||||
".bzr",
|
||||
@@ -223,7 +224,7 @@ indent-width = 4
|
||||
# Assume Python 3.8
|
||||
target-version = "py38"
|
||||
|
||||
[lint]
|
||||
[tool.ruff.lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = []
|
||||
@@ -235,7 +236,7 @@ unfixable = []
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[format]
|
||||
[tool.ruff.format]
|
||||
# Like Black, use double quotes for strings.
|
||||
quote-style = "double"
|
||||
|
||||
@@ -249,20 +250,11 @@ skip-magic-trailing-comma = false
|
||||
line-ending = "auto"
|
||||
```
|
||||
|
||||
Note that, in a `pyproject.toml`, each section header should be prefixed with `tool.ruff`. For
|
||||
example, `[lint]` should be replaced with `[tool.ruff.lint]`.
|
||||
|
||||
Some configuration options can be provided via dedicated command-line arguments, such as those
|
||||
related to rule enablement and disablement, file discovery, and logging level:
|
||||
Some configuration options can be provided via the command-line, such as those related to
|
||||
rule enablement and disablement, file discovery, and logging level:
|
||||
|
||||
```shell
|
||||
ruff check --select F401 --select F403 --quiet
|
||||
```
|
||||
|
||||
The remaining configuration options can be provided through a catch-all `--config` argument:
|
||||
|
||||
```shell
|
||||
ruff check --config "lint.per-file-ignores = {'some_file.py' = ['F841']}"
|
||||
ruff check path/to/code/ --select F401 --select F403 --quiet
|
||||
```
|
||||
|
||||
See `ruff help` for more on Ruff's top-level commands, or `ruff help check` and `ruff help format`
|
||||
@@ -272,7 +264,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 +421,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)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.3.5"
|
||||
version = "0.3.1"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -20,12 +20,10 @@ ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_notebook = { path = "../ruff_notebook" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast" }
|
||||
ruff_python_formatter = { path = "../ruff_python_formatter" }
|
||||
ruff_server = { path = "../ruff_server" }
|
||||
ruff_source_file = { path = "../ruff_source_file" }
|
||||
ruff_text_size = { path = "../ruff_text_size" }
|
||||
ruff_workspace = { path = "../ruff_workspace" }
|
||||
|
||||
anstream = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
argfile = { workspace = true }
|
||||
bincode = { workspace = true }
|
||||
@@ -35,14 +33,13 @@ chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "env", "wrap_help"] }
|
||||
clap_complete_command = { workspace = true }
|
||||
clearscreen = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
filetime = { workspace = true }
|
||||
ignore = { workspace = true }
|
||||
is-macro = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
log = { workspace = true }
|
||||
notify = { workspace = true }
|
||||
num_cpus = { workspace = true }
|
||||
owo-colors = { workspace = true }
|
||||
path-absolutize = { workspace = true, features = ["once_cell_cache"] }
|
||||
rayon = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
@@ -55,14 +52,15 @@ tempfile = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
tracing = { workspace = true, features = ["log"] }
|
||||
tracing-subscriber = { workspace = true, features = ["registry"] }
|
||||
tracing-tree = { workspace = true }
|
||||
walkdir = { workspace = true }
|
||||
wild = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Enable test rules during development
|
||||
ruff_linter = { path = "../ruff_linter", features = ["clap", "test-rules"] }
|
||||
assert_cmd = { workspace = true }
|
||||
# Avoid writing colored snapshots when running tests from the terminal
|
||||
colored = { workspace = true, features = ["no-color"]}
|
||||
insta = { workspace = true, features = ["filters", "json"] }
|
||||
insta-cmd = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::Arc;
|
||||
use anyhow::bail;
|
||||
use clap::builder::{TypedValueParser, ValueParserFactory};
|
||||
use clap::{command, Parser};
|
||||
use owo_colors::OwoColorize;
|
||||
use colored::Colorize;
|
||||
use path_absolutize::path_dedot;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -126,8 +126,6 @@ pub enum Command {
|
||||
GenerateShellCompletion { shell: clap_complete_command::Shell },
|
||||
/// Run the Ruff formatter on the given files or directories.
|
||||
Format(FormatCommand),
|
||||
/// Run the language server.
|
||||
Server(ServerCommand),
|
||||
/// Display Ruff's version
|
||||
Version {
|
||||
#[arg(long, value_enum, default_value = "text")]
|
||||
@@ -496,13 +494,6 @@ pub struct FormatCommand {
|
||||
pub range: Option<FormatRange>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, clap::Parser)]
|
||||
pub struct ServerCommand {
|
||||
/// Enable preview mode; required for regular operation
|
||||
#[arg(long)]
|
||||
pub(crate) preview: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||
pub enum HelpFormat {
|
||||
Text,
|
||||
@@ -1053,6 +1044,7 @@ pub enum FormatRangeParseError {
|
||||
|
||||
impl std::fmt::Display for FormatRangeParseError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let tip = " tip:".bold().green();
|
||||
match self {
|
||||
FormatRangeParseError::StartGreaterThanEnd(start, end) => {
|
||||
write!(
|
||||
@@ -1061,8 +1053,7 @@ impl std::fmt::Display for FormatRangeParseError {
|
||||
start_invalid=start.to_string().bold().yellow(),
|
||||
end_invalid=end.to_string().bold().yellow(),
|
||||
start=start.to_string().green().bold(),
|
||||
end=end.to_string().green().bold(),
|
||||
tip=" tip:".bold().green()
|
||||
end=end.to_string().green().bold()
|
||||
)
|
||||
}
|
||||
FormatRangeParseError::InvalidStart(inner) => inner.write(f, true),
|
||||
@@ -1148,8 +1139,7 @@ pub enum LineColumnParseError {
|
||||
|
||||
impl LineColumnParseError {
|
||||
fn write(&self, f: &mut std::fmt::Formatter, start_range: bool) -> std::fmt::Result {
|
||||
let tip = "tip:".bold();
|
||||
let tip = tip.green();
|
||||
let tip = "tip:".bold().green();
|
||||
|
||||
let range = if start_range { "start" } else { "end" };
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use ignore::Error;
|
||||
use log::{debug, error, warn};
|
||||
use owo_colors::OwoColorize;
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
@@ -226,7 +226,6 @@ mod test {
|
||||
use rustc_hash::FxHashMap;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use ruff_linter::colors;
|
||||
use ruff_linter::message::{Emitter, EmitterContext, TextEmitter};
|
||||
use ruff_linter::registry::Rule;
|
||||
use ruff_linter::settings::types::UnsafeFixes;
|
||||
@@ -253,7 +252,6 @@ mod test {
|
||||
for file in [&pyproject_toml, &python_file, ¬ebook] {
|
||||
fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.mode(0o000)
|
||||
.open(file)?;
|
||||
@@ -281,7 +279,7 @@ mod test {
|
||||
UnsafeFixes::Enabled,
|
||||
)
|
||||
.unwrap();
|
||||
let mut output = colors::none(Vec::new());
|
||||
let mut output = Vec::new();
|
||||
|
||||
TextEmitter::default()
|
||||
.with_show_fix_status(true)
|
||||
@@ -292,7 +290,7 @@ mod test {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let messages = String::from_utf8(output.into_inner()).unwrap();
|
||||
let messages = String::from_utf8(output).unwrap();
|
||||
|
||||
insta::with_settings!({
|
||||
omit_expression => true,
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::fs::remove_dir_all;
|
||||
use std::io::{self, BufWriter, Write};
|
||||
|
||||
use anyhow::Result;
|
||||
use owo_colors::OwoColorize;
|
||||
use colored::Colorize;
|
||||
use path_absolutize::path_dedot;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Result;
|
||||
use colored::Colorize;
|
||||
use itertools::Itertools;
|
||||
use log::{error, warn};
|
||||
use owo_colors::OwoColorize;
|
||||
use rayon::iter::Either::{Left, Right};
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use rustc_hash::FxHashSet;
|
||||
@@ -16,7 +16,6 @@ use thiserror::Error;
|
||||
use tracing::debug;
|
||||
|
||||
use ruff_diagnostics::SourceMap;
|
||||
use ruff_linter::colors;
|
||||
use ruff_linter::fs;
|
||||
use ruff_linter::logging::{DisplayParseError, LogLevel};
|
||||
use ruff_linter::registry::Rule;
|
||||
@@ -190,10 +189,10 @@ pub(crate) fn format(
|
||||
match mode {
|
||||
FormatMode::Write => {}
|
||||
FormatMode::Check => {
|
||||
results.write_changed(&mut colors::auto(stdout()).lock())?;
|
||||
results.write_changed(&mut stdout().lock())?;
|
||||
}
|
||||
FormatMode::Diff => {
|
||||
results.write_diff(&mut colors::auto(stdout()).lock())?;
|
||||
results.write_diff(&mut stdout().lock())?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,9 +200,9 @@ pub(crate) fn format(
|
||||
if config_arguments.log_level >= LogLevel::Default {
|
||||
if mode.is_diff() {
|
||||
// Allow piping the diff to e.g. a file by writing the summary to stderr
|
||||
results.write_summary(&mut colors::auto(stderr()).lock())?;
|
||||
results.write_summary(&mut stderr().lock())?;
|
||||
} else {
|
||||
results.write_summary(&mut colors::auto(stdout()).lock())?;
|
||||
results.write_summary(&mut stdout().lock())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::path::Path;
|
||||
use anyhow::Result;
|
||||
use log::error;
|
||||
|
||||
use ruff_linter::colors;
|
||||
use ruff_linter::source_kind::SourceKind;
|
||||
use ruff_python_ast::{PySourceType, SourceType};
|
||||
use ruff_workspace::resolver::{match_exclusion, python_file_at_path, Resolver};
|
||||
@@ -112,7 +111,7 @@ fn format_source_code(
|
||||
match &formatted {
|
||||
FormattedSource::Formatted(formatted) => match mode {
|
||||
FormatMode::Write => {
|
||||
let mut writer = colors::auto(stdout()).lock();
|
||||
let mut writer = stdout().lock();
|
||||
formatted
|
||||
.write(&mut writer)
|
||||
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;
|
||||
@@ -121,7 +120,7 @@ fn format_source_code(
|
||||
FormatMode::Diff => {
|
||||
use std::io::Write;
|
||||
write!(
|
||||
&mut colors::auto(stdout()).lock(),
|
||||
&mut stdout().lock(),
|
||||
"{}",
|
||||
source_kind.diff(formatted, path).unwrap()
|
||||
)
|
||||
@@ -131,7 +130,7 @@ fn format_source_code(
|
||||
FormattedSource::Unchanged => {
|
||||
// Write to stdout regardless of whether the source was formatted
|
||||
if mode.is_write() {
|
||||
let mut writer = colors::auto(stdout()).lock();
|
||||
let mut writer = stdout().lock();
|
||||
source_kind
|
||||
.write(&mut writer)
|
||||
.map_err(|err| FormatCommandError::Write(path.map(Path::to_path_buf), err))?;
|
||||
|
||||
@@ -7,7 +7,6 @@ pub(crate) mod format;
|
||||
pub(crate) mod format_stdin;
|
||||
pub(crate) mod linter;
|
||||
pub(crate) mod rule;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod show_files;
|
||||
pub(crate) mod show_settings;
|
||||
pub(crate) mod version;
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
use crate::ExitStatus;
|
||||
use anyhow::Result;
|
||||
use ruff_linter::logging::LogLevel;
|
||||
use ruff_server::Server;
|
||||
use tracing::{level_filters::LevelFilter, metadata::Level, subscriber::Interest, Metadata};
|
||||
use tracing_subscriber::{
|
||||
layer::{Context, Filter, SubscriberExt},
|
||||
Layer, Registry,
|
||||
};
|
||||
use tracing_tree::time::Uptime;
|
||||
|
||||
pub(crate) fn run_server(
|
||||
preview: bool,
|
||||
worker_threads: NonZeroUsize,
|
||||
log_level: LogLevel,
|
||||
) -> Result<ExitStatus> {
|
||||
if !preview {
|
||||
tracing::error!("--preview needs to be provided as a command line argument while the server is still unstable.\nFor example: `ruff server --preview`");
|
||||
return Ok(ExitStatus::Error);
|
||||
}
|
||||
let trace_level = if log_level == LogLevel::Verbose {
|
||||
Level::TRACE
|
||||
} else {
|
||||
Level::DEBUG
|
||||
};
|
||||
|
||||
let subscriber = Registry::default().with(
|
||||
tracing_tree::HierarchicalLayer::default()
|
||||
.with_indent_lines(true)
|
||||
.with_indent_amount(2)
|
||||
.with_bracketed_fields(true)
|
||||
.with_targets(true)
|
||||
.with_writer(|| Box::new(std::io::stderr()))
|
||||
.with_timer(Uptime::default())
|
||||
.with_filter(LoggingFilter { trace_level }),
|
||||
);
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)?;
|
||||
|
||||
let server = Server::new(worker_threads)?;
|
||||
|
||||
server.run().map(|()| ExitStatus::Success)
|
||||
}
|
||||
|
||||
struct LoggingFilter {
|
||||
trace_level: Level,
|
||||
}
|
||||
|
||||
impl LoggingFilter {
|
||||
fn is_enabled(&self, meta: &Metadata<'_>) -> bool {
|
||||
let filter = if meta.target().starts_with("ruff") {
|
||||
self.trace_level
|
||||
} else {
|
||||
Level::INFO
|
||||
};
|
||||
|
||||
meta.level() <= &filter
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Filter<S> for LoggingFilter {
|
||||
fn enabled(&self, meta: &Metadata<'_>, _cx: &Context<'_, S>) -> bool {
|
||||
self.is_enabled(meta)
|
||||
}
|
||||
|
||||
fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
|
||||
if self.is_enabled(meta) {
|
||||
Interest::always()
|
||||
} else {
|
||||
Interest::never()
|
||||
}
|
||||
}
|
||||
|
||||
fn max_level_hint(&self) -> Option<LevelFilter> {
|
||||
Some(LevelFilter::from_level(self.trace_level))
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,11 @@ use std::ops::{Add, AddAssign};
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use colored::Colorize;
|
||||
use log::{debug, error, warn};
|
||||
use owo_colors::OwoColorize;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_linter::colors;
|
||||
use ruff_linter::linter::{lint_fix, lint_only, FixTable, FixerResult, LinterResult, ParseSource};
|
||||
use ruff_linter::logging::DisplayParseError;
|
||||
use ruff_linter::message::Message;
|
||||
@@ -445,7 +444,7 @@ pub(crate) fn lint_stdin(
|
||||
// But only write a diff if it's non-empty.
|
||||
if !fixed.is_empty() {
|
||||
write!(
|
||||
&mut colors::auto(io::stdout()).lock(),
|
||||
&mut io::stdout().lock(),
|
||||
"{}",
|
||||
source_kind.diff(&transformed, path).unwrap()
|
||||
)?;
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
|
||||
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;
|
||||
|
||||
use anyhow::Result;
|
||||
use args::{GlobalConfigArgs, ServerCommand};
|
||||
use args::GlobalConfigArgs;
|
||||
use clap::CommandFactory;
|
||||
use colored::Colorize;
|
||||
use log::warn;
|
||||
use notify::{recommended_watcher, RecursiveMode, Watcher};
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
use ruff_linter::colors;
|
||||
use ruff_linter::logging::{set_up_logging, LogLevel};
|
||||
use ruff_linter::settings::flags::FixMode;
|
||||
use ruff_linter::settings::types::SerializationFormat;
|
||||
@@ -146,6 +144,10 @@ pub fn run(
|
||||
}));
|
||||
}
|
||||
|
||||
// Enabled ANSI colors on Windows 10.
|
||||
#[cfg(windows)]
|
||||
assert!(colored::control::set_virtual_terminal(true).is_ok());
|
||||
|
||||
set_up_logging(global_options.log_level())?;
|
||||
|
||||
if let Some(deprecated_alias_warning) = deprecated_alias_warning {
|
||||
@@ -188,7 +190,6 @@ pub fn run(
|
||||
}
|
||||
Command::Check(args) => check(args, global_options),
|
||||
Command::Format(args) => format(args, global_options),
|
||||
Command::Server(args) => server(args, global_options.log_level()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,17 +203,6 @@ fn format(args: FormatCommand, global_options: GlobalConfigArgs) -> Result<ExitS
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<ExitStatus> {
|
||||
let (cli, config_arguments) = args.partition(global_options)?;
|
||||
|
||||
@@ -222,12 +212,13 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
|
||||
|
||||
let mut writer: Box<dyn Write> = match cli.output_file {
|
||||
Some(path) if !cli.watch => {
|
||||
colored::control::set_override(false);
|
||||
let file = File::create(path)?;
|
||||
Box::new(BufWriter::new(colors::none(file)))
|
||||
Box::new(BufWriter::new(file))
|
||||
}
|
||||
_ => Box::new(BufWriter::new(colors::auto(io::stdout()))),
|
||||
_ => Box::new(BufWriter::new(io::stdout())),
|
||||
};
|
||||
let stderr_writer = Box::new(BufWriter::new(colors::auto(io::stderr())));
|
||||
let stderr_writer = Box::new(BufWriter::new(io::stderr()));
|
||||
|
||||
let is_stdin = is_stdin(&cli.files, cli.stdin_filename.as_deref());
|
||||
let files = resolve_default_files(cli.files, is_stdin);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
use anstream::eprintln;
|
||||
use clap::{Parser, Subcommand};
|
||||
use owo_colors::OwoColorize;
|
||||
use colored::Colorize;
|
||||
|
||||
use ruff::args::{Args, Command};
|
||||
use ruff::{run, ExitStatus};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -5,8 +5,8 @@ use std::io::Write;
|
||||
|
||||
use anyhow::Result;
|
||||
use bitflags::bitflags;
|
||||
use colored::Colorize;
|
||||
use itertools::{iterate, Itertools};
|
||||
use owo_colors::OwoColorize;
|
||||
use serde::Serialize;
|
||||
|
||||
use ruff_linter::fs::relativize_path;
|
||||
@@ -118,8 +118,6 @@ impl Printer {
|
||||
} else if remaining > 0 {
|
||||
let s = if remaining == 1 { "" } else { "s" };
|
||||
writeln!(writer, "Found {remaining} error{s}.")?;
|
||||
} else if remaining == 0 {
|
||||
writeln!(writer, "All checks passed!")?;
|
||||
}
|
||||
|
||||
if let Some(fixables) = fixables {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -23,7 +23,7 @@ fn default_options() {
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
def foo(arg1, arg2,):
|
||||
print('Shouldn\'t change quotes')
|
||||
print('Should\'t change quotes')
|
||||
|
||||
|
||||
if condition:
|
||||
@@ -38,7 +38,7 @@ if condition:
|
||||
arg1,
|
||||
arg2,
|
||||
):
|
||||
print("Shouldn't change quotes")
|
||||
print("Should't change quotes")
|
||||
|
||||
|
||||
if condition:
|
||||
@@ -523,7 +523,7 @@ from module import =
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to parse main.py:2:20: Unexpected token '='
|
||||
error: Failed to parse main.py:2:20: Unexpected token =
|
||||
"###);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -101,7 +101,6 @@ fn stdin_success() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -223,7 +222,6 @@ fn stdin_source_type_pyi() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -592,7 +590,6 @@ fn stdin_fix_when_no_issues_should_still_print_contents() {
|
||||
print(sys.version)
|
||||
|
||||
----- stderr -----
|
||||
All checks passed!
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -731,11 +728,11 @@ fn stdin_parse_error() {
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
-:1:17: E999 SyntaxError: Unexpected token '='
|
||||
-:1:17: E999 SyntaxError: Unexpected token =
|
||||
Found 1 error.
|
||||
|
||||
----- stderr -----
|
||||
error: Failed to parse at 1:17: Unexpected token '='
|
||||
error: Failed to parse at 1:17: Unexpected token =
|
||||
"###);
|
||||
}
|
||||
|
||||
@@ -1026,7 +1023,6 @@ fn preview_disabled_direct() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: Selection `RUF911` has no effect because preview is not enabled.
|
||||
@@ -1043,7 +1039,6 @@ fn preview_disabled_prefix_empty() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: Selection `RUF91` has no effect because preview is not enabled.
|
||||
@@ -1060,7 +1055,6 @@ fn preview_disabled_does_not_warn_for_empty_ignore_selections() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -1076,7 +1070,6 @@ fn preview_disabled_does_not_warn_for_empty_fixable_selections() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -1182,7 +1175,6 @@ fn removed_indirect() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -1213,7 +1205,6 @@ fn redirect_indirect() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -1316,7 +1307,6 @@ fn deprecated_indirect_preview_enabled() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
@@ -1353,7 +1343,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)?;
|
||||
@@ -1394,7 +1383,6 @@ fn unreadable_dir() -> Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: Encountered error: Permission denied (os error 13)
|
||||
@@ -1909,7 +1897,6 @@ def log(x, base) -> float:
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
|
||||
@@ -496,7 +496,6 @@ ignore = ["D203", "D212"]
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
warning: No Python files found under the given path(s)
|
||||
@@ -834,7 +833,6 @@ fn complex_config_setting_overridden_via_cli() -> Result<()> {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
All checks passed!
|
||||
|
||||
----- stderr -----
|
||||
"###);
|
||||
|
||||
@@ -34,11 +34,6 @@ marking it as unused, as in:
|
||||
from module import member as member
|
||||
```
|
||||
|
||||
## Fix safety
|
||||
|
||||
When `ignore_init_module_imports` is disabled, fixes can remove for unused imports in `__init__` files.
|
||||
These fixes are considered unsafe because they can change the public interface.
|
||||
|
||||
## Example
|
||||
```python
|
||||
import numpy as np # unused import
|
||||
|
||||
@@ -52,6 +52,7 @@ file_resolver.exclude = [
|
||||
file_resolver.extend_exclude = [
|
||||
"crates/ruff_linter/resources/",
|
||||
"crates/ruff_python_formatter/resources/",
|
||||
"crates/ruff_python_parser/resources/",
|
||||
]
|
||||
file_resolver.force_exclude = false
|
||||
file_resolver.include = [
|
||||
@@ -201,7 +202,7 @@ linter.allowed_confusables = []
|
||||
linter.builtins = []
|
||||
linter.dummy_variable_rgx = ^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$
|
||||
linter.external = []
|
||||
linter.ignore_init_module_imports = true
|
||||
linter.ignore_init_module_imports = false
|
||||
linter.logger_objects = []
|
||||
linter.namespace_packages = []
|
||||
linter.src = [
|
||||
@@ -231,7 +232,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
|
||||
@@ -241,22 +242,7 @@ linter.flake8_gettext.functions_names = [
|
||||
ngettext,
|
||||
]
|
||||
linter.flake8_implicit_str_concat.allow_multiline = true
|
||||
linter.flake8_import_conventions.aliases = {
|
||||
altair = alt,
|
||||
holoviews = hv,
|
||||
matplotlib = mpl,
|
||||
matplotlib.pyplot = plt,
|
||||
networkx = nx,
|
||||
numpy = np,
|
||||
pandas = pd,
|
||||
panel = pn,
|
||||
plotly.express = px,
|
||||
polars = pl,
|
||||
pyarrow = pa,
|
||||
seaborn = sns,
|
||||
tensorflow = tf,
|
||||
tkinter = tk,
|
||||
}
|
||||
linter.flake8_import_conventions.aliases = {"matplotlib": "mpl", "matplotlib.pyplot": "plt", "pandas": "pd", "seaborn": "sns", "tensorflow": "tf", "networkx": "nx", "plotly.express": "px", "polars": "pl", "numpy": "np", "panel": "pn", "pyarrow": "pa", "altair": "alt", "tkinter": "tk", "holoviews": "hv"}
|
||||
linter.flake8_import_conventions.banned_aliases = {}
|
||||
linter.flake8_import_conventions.banned_from = []
|
||||
linter.flake8_pytest_style.fixture_parentheses = true
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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})"));
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ tracing = { workspace = true }
|
||||
unicode-width = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { workspace = true }
|
||||
|
||||
[features]
|
||||
serde = ["dep:serde", "ruff_text_size/serde"]
|
||||
|
||||
@@ -37,7 +37,7 @@ pub trait Buffer {
|
||||
#[doc(hidden)]
|
||||
fn elements(&self) -> &[FormatElement];
|
||||
|
||||
/// Glue for usage of the [`write!`] macro with implementers of this trait.
|
||||
/// Glue for usage of the [`write!`] macro with implementors of this trait.
|
||||
///
|
||||
/// This method should generally not be invoked manually, but rather through the [`write!`] macro itself.
|
||||
///
|
||||
|
||||
@@ -545,10 +545,6 @@ impl PrintedRange {
|
||||
&self.code
|
||||
}
|
||||
|
||||
pub fn into_code(self) -> String {
|
||||
self.code
|
||||
}
|
||||
|
||||
/// The range the formatted code corresponds to in the source document.
|
||||
pub fn source_range(&self) -> TextRange {
|
||||
self.source_range
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.3.5"
|
||||
version = "0.3.1"
|
||||
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"] }
|
||||
@@ -30,11 +31,11 @@ ruff_text_size = { path = "../ruff_text_size" }
|
||||
|
||||
aho-corasick = { workspace = true }
|
||||
annotate-snippets = { workspace = true, features = ["color"] }
|
||||
anstream = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive", "string"], optional = true }
|
||||
colored = { workspace = true }
|
||||
fern = { workspace = true }
|
||||
glob = { workspace = true }
|
||||
globset = { workspace = true }
|
||||
@@ -47,7 +48,6 @@ log = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
natord = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
owo-colors = { workspace = true }
|
||||
path-absolutize = { workspace = true, features = [
|
||||
"once_cell_cache",
|
||||
"use_unix_paths_on_wasm",
|
||||
@@ -75,7 +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 = []
|
||||
|
||||
@@ -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",
|
||||
# ]
|
||||
|
||||
@@ -18,7 +18,3 @@ func("0.0.0.0")
|
||||
def my_func():
|
||||
x = "0.0.0.0"
|
||||
print(x)
|
||||
|
||||
|
||||
# Implicit string concatenation
|
||||
"0.0.0.0" f"0.0.0.0{expr}0.0.0.0"
|
||||
|
||||
@@ -18,13 +18,6 @@ with open("/dev/shm/unit/test", "w") as f:
|
||||
with open("/foo/bar", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
# Implicit string concatenation
|
||||
with open("/tmp/" "abc", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
with open("/tmp/abc" f"/tmp/abc", "w") as f:
|
||||
f.write("def")
|
||||
|
||||
# Using `tempfile` module should be ok
|
||||
import tempfile
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import os
|
||||
import random
|
||||
|
||||
import a_lib
|
||||
|
||||
# OK
|
||||
random.SystemRandom()
|
||||
|
||||
# Errors
|
||||
random.Random()
|
||||
random.random()
|
||||
random.randrange()
|
||||
random.randint()
|
||||
random.choice()
|
||||
random.choices()
|
||||
random.uniform()
|
||||
random.triangular()
|
||||
random.randbytes()
|
||||
|
||||
# Unrelated
|
||||
os.urandom()
|
||||
a_lib.random()
|
||||
@@ -1,47 +1,52 @@
|
||||
import crypt
|
||||
import hashlib
|
||||
from hashlib import new as hashlib_new
|
||||
from hashlib import sha1 as hashlib_sha1
|
||||
|
||||
# Errors
|
||||
# Invalid
|
||||
|
||||
hashlib.new('md5')
|
||||
|
||||
hashlib.new('md4', b'test')
|
||||
|
||||
hashlib.new(name='md5', data=b'test')
|
||||
|
||||
hashlib.new('MD4', data=b'test')
|
||||
|
||||
hashlib.new('sha1')
|
||||
|
||||
hashlib.new('sha1', data=b'test')
|
||||
|
||||
hashlib.new('sha', data=b'test')
|
||||
|
||||
hashlib.new(name='SHA', data=b'test')
|
||||
|
||||
hashlib.sha(data=b'test')
|
||||
|
||||
hashlib.md5()
|
||||
|
||||
hashlib_new('sha1')
|
||||
|
||||
hashlib_sha1('sha1')
|
||||
|
||||
# usedforsecurity arg only available in Python 3.9+
|
||||
hashlib.new('sha1', usedforsecurity=True)
|
||||
|
||||
crypt.crypt("test", salt=crypt.METHOD_CRYPT)
|
||||
crypt.crypt("test", salt=crypt.METHOD_MD5)
|
||||
crypt.crypt("test", salt=crypt.METHOD_BLOWFISH)
|
||||
crypt.crypt("test", crypt.METHOD_BLOWFISH)
|
||||
# Valid
|
||||
|
||||
crypt.mksalt(crypt.METHOD_CRYPT)
|
||||
crypt.mksalt(crypt.METHOD_MD5)
|
||||
crypt.mksalt(crypt.METHOD_BLOWFISH)
|
||||
|
||||
# OK
|
||||
hashlib.new('sha256')
|
||||
|
||||
hashlib.new('SHA512')
|
||||
|
||||
hashlib.sha256(data=b'test')
|
||||
|
||||
# usedforsecurity arg only available in Python 3.9+
|
||||
hashlib_new(name='sha1', usedforsecurity=False)
|
||||
|
||||
# usedforsecurity arg only available in Python 3.9+
|
||||
hashlib_sha1(name='sha1', usedforsecurity=False)
|
||||
|
||||
# usedforsecurity arg only available in Python 3.9+
|
||||
hashlib.md4(usedforsecurity=False)
|
||||
|
||||
# usedforsecurity arg only available in Python 3.9+
|
||||
hashlib.new(name='sha256', usedforsecurity=False)
|
||||
|
||||
crypt.crypt("test")
|
||||
crypt.crypt("test", salt=crypt.METHOD_SHA256)
|
||||
crypt.crypt("test", salt=crypt.METHOD_SHA512)
|
||||
|
||||
crypt.mksalt()
|
||||
crypt.mksalt(crypt.METHOD_SHA256)
|
||||
crypt.mksalt(crypt.METHOD_SHA512)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import commands
|
||||
import popen2
|
||||
@@ -17,8 +16,6 @@ popen2.Popen3("true")
|
||||
popen2.Popen4("true")
|
||||
commands.getoutput("true")
|
||||
commands.getstatusoutput("true")
|
||||
subprocess.getoutput("true")
|
||||
subprocess.getstatusoutput("true")
|
||||
|
||||
|
||||
# Check command argument looks unsafe.
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
# Errors
|
||||
User.objects.filter(username='admin').extra(dict(could_be='insecure'))
|
||||
User.objects.filter(username='admin').extra(select=dict(could_be='insecure'))
|
||||
User.objects.filter(username='admin').extra(select={'test': '%secure' % 'nos'})
|
||||
User.objects.filter(username='admin').extra(select={'test': '{}secure'.format('nos')})
|
||||
User.objects.filter(username='admin').extra(where=['%secure' % 'nos'])
|
||||
User.objects.filter(username='admin').extra(where=['{}secure'.format('no')])
|
||||
|
||||
query = '"username") AS "username", * FROM "auth_user" WHERE 1=1 OR "username"=? --'
|
||||
User.objects.filter(username='admin').extra(select={'test': query})
|
||||
|
||||
where_var = ['1=1) OR 1=1 AND (1=1']
|
||||
User.objects.filter(username='admin').extra(where=where_var)
|
||||
|
||||
where_str = '1=1) OR 1=1 AND (1=1'
|
||||
User.objects.filter(username='admin').extra(where=[where_str])
|
||||
|
||||
tables_var = ['django_content_type" WHERE "auth_user"."username"="admin']
|
||||
User.objects.all().extra(tables=tables_var).distinct()
|
||||
|
||||
tables_str = 'django_content_type" WHERE "auth_user"."username"="admin'
|
||||
User.objects.all().extra(tables=[tables_str]).distinct()
|
||||
|
||||
# OK
|
||||
User.objects.filter(username='admin').extra(
|
||||
select={'test': 'secure'},
|
||||
where=['secure'],
|
||||
tables=['secure']
|
||||
)
|
||||
User.objects.filter(username='admin').extra({'test': 'secure'})
|
||||
User.objects.filter(username='admin').extra(select={'test': 'secure'})
|
||||
User.objects.filter(username='admin').extra(where=['secure'])
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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')} }")
|
||||
|
||||
@@ -16,11 +16,3 @@ tuple( # comment
|
||||
tuple([ # comment
|
||||
1, 2
|
||||
])
|
||||
|
||||
tuple((
|
||||
1,
|
||||
))
|
||||
|
||||
t6 = tuple([1])
|
||||
t7 = tuple((1,))
|
||||
t8 = tuple([1,])
|
||||
|
||||
@@ -14,6 +14,9 @@ reversed(sorted(x, reverse=not x))
|
||||
reversed(sorted(i for i in range(42)))
|
||||
reversed(sorted((i for i in range(42)), reverse=True))
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/10335
|
||||
reversed(sorted([1, 2, 3], reverse=False or True))
|
||||
reversed(sorted([1, 2, 3], reverse=(False or True)))
|
||||
|
||||
def reversed(*args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
reversed(sorted(x, reverse=True))
|
||||
|
||||
@@ -13,10 +13,6 @@ all(x.id for x in bar)
|
||||
all(x.id for x in bar)
|
||||
any(x.id for x in bar)
|
||||
all((x.id for x in bar))
|
||||
# we don't lint on these in stable yet
|
||||
sum([x.val for x in bar])
|
||||
min([x.val for x in bar])
|
||||
max([x.val for x in bar])
|
||||
|
||||
|
||||
async def f() -> bool:
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
sum([x.val for x in bar])
|
||||
min([x.val for x in bar])
|
||||
max([x.val for x in bar])
|
||||
|
||||
# Ok
|
||||
sum(x.val for x in bar)
|
||||
min(x.val for x in bar)
|
||||
max(x.val for x in bar)
|
||||
@@ -1,3 +0,0 @@
|
||||
# no lint if shadowed
|
||||
def all(x): pass
|
||||
all([x.id for x in bar])
|
||||
@@ -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")
|
||||
|
||||
@@ -21,7 +21,6 @@ def unconventional_aliases():
|
||||
import tkinter as tkr
|
||||
import networkx as nxy
|
||||
|
||||
|
||||
def conventional_aliases():
|
||||
import altair as alt
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
def no_alias():
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def conventional_alias():
|
||||
from django.conf import settings as settings
|
||||
|
||||
|
||||
def unconventional_alias():
|
||||
from django.conf import settings as s
|
||||
@@ -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
|
||||
|
||||
@@ -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."""
|
||||
...
|
||||
|
||||
@@ -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']
|
||||
@@ -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']
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -64,5 +64,3 @@ def not_warnings_dot_deprecated(
|
||||
"Not warnings.deprecated, so this one *should* lead to PYI053 in a stub!" # Error: PYI053
|
||||
)
|
||||
def not_a_deprecated_function() -> None: ...
|
||||
|
||||
fbaz: str = f"51 character {foo} stringgggggggggggggggggggggggggg" # Error: PYI053
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
...
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,5 +0,0 @@
|
||||
""'Start with empty string' ' and lint docstring safely'
|
||||
|
||||
def foo():
|
||||
pass
|
||||
""" this is not a docstring """
|
||||
@@ -1,5 +0,0 @@
|
||||
"Do not"' start with empty string' ' and lint docstring safely'
|
||||
|
||||
def foo():
|
||||
pass
|
||||
""" this is not a docstring """
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,5 +0,0 @@
|
||||
''"Start with empty string" ' and lint docstring safely'
|
||||
|
||||
def foo():
|
||||
pass
|
||||
""" this is not a docstring """
|
||||
@@ -1,5 +0,0 @@
|
||||
'Do not'" start with empty string" ' and lint docstring safely'
|
||||
|
||||
def foo():
|
||||
pass
|
||||
""" this is not a docstring """
|
||||
@@ -1,7 +0,0 @@
|
||||
"""This is a docstring."""
|
||||
|
||||
this_is_an_inline_string = "double quote string"
|
||||
|
||||
this_is_a_multiline_string = """
|
||||
double quote string
|
||||
"""
|
||||
@@ -1,2 +0,0 @@
|
||||
s = ""'Start with empty string' ' and lint docstring safely'
|
||||
s = "Do not"' start with empty string' ' and lint docstring safely'
|
||||
@@ -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']"
|
||||
|
||||
@@ -40,7 +40,4 @@ f"\'normal\' {f"\'nested\' {"other"} 'single quotes'"} normal" # Q004
|
||||
|
||||
# Make sure we do not unescape quotes
|
||||
this_is_fine = "This is an \\'escaped\\' quote"
|
||||
this_should_raise_Q004 = "This is an \\\'escaped\\\' quote with an extra backslash" # Q004
|
||||
|
||||
# Invalid escapes in bytestrings are also triggered:
|
||||
x = b"\xe7\xeb\x0c\xa1\x1b\x83tN\xce=x\xe9\xbe\x01\xb9\x13B_\xba\xe7\x0c2\xce\'rm\x0e\xcd\xe9.\xf8\xd2" # Q004
|
||||
this_should_raise_Q004 = "This is an \\\'escaped\\\' quote with an extra backslash"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
s = ''"Start with empty string" ' and lint docstring safely'
|
||||
s = 'Do not'" start with empty string" ' and lint docstring safely'
|
||||
@@ -406,18 +406,3 @@ def foo():
|
||||
with contextlib.suppress(Exception):
|
||||
y = 2
|
||||
return y
|
||||
|
||||
|
||||
# See: https://github.com/astral-sh/ruff/issues/10732
|
||||
def func(a: dict[str, int]) -> list[dict[str, int]]:
|
||||
services: list[dict[str, int]]
|
||||
if "services" in a:
|
||||
services = a["services"]
|
||||
return services
|
||||
|
||||
|
||||
# See: https://github.com/astral-sh/ruff/issues/10732
|
||||
def func(a: dict[str, int]) -> list[dict[str, int]]:
|
||||
if "services" in a:
|
||||
services = a["services"]
|
||||
return services
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,7 +10,7 @@ async def func():
|
||||
|
||||
trio.sleep(0) # TRIO115
|
||||
foo = 0
|
||||
trio.sleep(foo) # OK
|
||||
trio.sleep(foo) # TRIO115
|
||||
trio.sleep(1) # OK
|
||||
time.sleep(0) # OK
|
||||
|
||||
@@ -20,26 +20,26 @@ async def func():
|
||||
trio.sleep(bar)
|
||||
|
||||
x, y = 0, 2000
|
||||
trio.sleep(x) # OK
|
||||
trio.sleep(x) # TRIO115
|
||||
trio.sleep(y) # OK
|
||||
|
||||
(a, b, [c, (d, e)]) = (1, 2, (0, [4, 0]))
|
||||
trio.sleep(c) # OK
|
||||
trio.sleep(c) # TRIO115
|
||||
trio.sleep(d) # OK
|
||||
trio.sleep(e) # OK
|
||||
trio.sleep(e) # TRIO115
|
||||
|
||||
m_x, m_y = 0
|
||||
trio.sleep(m_y) # OK
|
||||
trio.sleep(m_x) # OK
|
||||
|
||||
m_a = m_b = 0
|
||||
trio.sleep(m_a) # OK
|
||||
trio.sleep(m_b) # OK
|
||||
trio.sleep(m_a) # TRIO115
|
||||
trio.sleep(m_b) # TRIO115
|
||||
|
||||
m_c = (m_d, m_e) = (0, 0)
|
||||
trio.sleep(m_c) # OK
|
||||
trio.sleep(m_d) # OK
|
||||
trio.sleep(m_e) # OK
|
||||
trio.sleep(m_d) # TRIO115
|
||||
trio.sleep(m_e) # TRIO115
|
||||
|
||||
|
||||
def func():
|
||||
@@ -63,16 +63,4 @@ def func():
|
||||
import trio
|
||||
|
||||
if (walrus := 0) == 0:
|
||||
trio.sleep(walrus) # OK
|
||||
|
||||
|
||||
def func():
|
||||
import trio
|
||||
|
||||
async def main() -> None:
|
||||
sleep = 0
|
||||
for _ in range(2):
|
||||
await trio.sleep(sleep) # OK
|
||||
sleep = 10
|
||||
|
||||
trio.run(main)
|
||||
trio.sleep(walrus) # TRIO115
|
||||
|
||||
@@ -3,5 +3,3 @@ import ruff
|
||||
import leading_prefix
|
||||
import os
|
||||
from . import leading_prefix
|
||||
from .. import trailing_prefix
|
||||
from ruff import check
|
||||
|
||||
@@ -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 * π * ℏ
|
||||
@@ -104,5 +104,3 @@ def func():
|
||||
np.unicode_("asf")
|
||||
|
||||
np.who()
|
||||
|
||||
np.row_stack(([1,2], [3,4]))
|
||||
|
||||
@@ -21,10 +21,3 @@ class D(TypedDict):
|
||||
mixedCase: bool
|
||||
_mixedCase: list
|
||||
mixed_Case: set
|
||||
|
||||
class E(D):
|
||||
lower: int
|
||||
CONSTANT: str
|
||||
mixedCase: bool
|
||||
_mixedCase: list
|
||||
mixed_Case: set
|
||||
|
||||
@@ -153,9 +153,3 @@ ham[lower +1 :, "columnname"]
|
||||
|
||||
#: E203:1:13
|
||||
ham[lower + 1 :, "columnname"]
|
||||
|
||||
#: Okay
|
||||
f"{ham[lower +1 :, "columnname"]}"
|
||||
|
||||
#: E203:1:13
|
||||
f"{ham[lower + 1 :, "columnname"]}"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
a = (1 or)
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
"""Test where the error is after the module's docstring."""
|
||||
|
||||
def fn():
|
||||
pass
|
||||
@@ -1,4 +0,0 @@
|
||||
"Test where the first line is a comment, " + "and the rule violation follows it."
|
||||
|
||||
def fn():
|
||||
pass
|
||||
@@ -1,5 +0,0 @@
|
||||
def fn1():
|
||||
pass
|
||||
|
||||
def fn2():
|
||||
pass
|
||||
@@ -1,4 +0,0 @@
|
||||
print("Test where the first line is a statement, and the rule violation follows it.")
|
||||
|
||||
def fn():
|
||||
pass
|
||||
@@ -1,6 +0,0 @@
|
||||
# Test where the first line is a comment, and the rule violation follows it.
|
||||
|
||||
|
||||
|
||||
def fn():
|
||||
pass
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user