Compare commits
1 Commits
cjm/pep613
...
david/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6954063c1 |
@@ -10,7 +10,7 @@ indent_style = space
|
||||
insert_final_newline = true
|
||||
indent_size = 2
|
||||
|
||||
[*.{rs,py,pyi,toml}]
|
||||
[*.{rs,py,pyi}]
|
||||
indent_size = 4
|
||||
|
||||
[*.snap]
|
||||
@@ -18,3 +18,6 @@ trim_trailing_whitespace = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = 100
|
||||
|
||||
[*.toml]
|
||||
indent_size = 4
|
||||
52
.github/workflows/ci.yaml
vendored
52
.github/workflows/ci.yaml
vendored
@@ -142,7 +142,7 @@ jobs:
|
||||
env:
|
||||
MERGE_BASE: ${{ steps.merge_base.outputs.sha }}
|
||||
run: |
|
||||
if git diff --quiet "${MERGE_BASE}...HEAD" -- 'python/py-fuzzer/**' \
|
||||
if git diff --quiet "${MERGE_BASE}...HEAD" -- 'python/py_fuzzer/**' \
|
||||
; then
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
@@ -361,37 +361,6 @@ jobs:
|
||||
cargo nextest run --all-features --profile ci
|
||||
cargo test --all-features --doc
|
||||
|
||||
cargo-test-macos:
|
||||
name: "cargo test (macos)"
|
||||
runs-on: macos-latest
|
||||
needs: determine_changes
|
||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-test') && (needs.determine_changes.outputs.code == 'true' || github.ref == 'refs/heads/main') }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install uv"
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
||||
with:
|
||||
enable-cache: "true"
|
||||
- name: "Run tests"
|
||||
shell: bash
|
||||
env:
|
||||
NEXTEST_PROFILE: "ci"
|
||||
run: |
|
||||
cargo nextest run --all-features --profile ci
|
||||
cargo test --all-features --doc
|
||||
|
||||
cargo-test-wasm:
|
||||
name: "cargo test (wasm)"
|
||||
runs-on: ubuntu-latest
|
||||
@@ -422,6 +391,23 @@ jobs:
|
||||
cd crates/ty_wasm
|
||||
wasm-pack test --node
|
||||
|
||||
cargo-build-release:
|
||||
name: "cargo build (release)"
|
||||
runs-on: macos-latest
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Build"
|
||||
run: cargo build --release --locked
|
||||
|
||||
cargo-build-msrv:
|
||||
name: "cargo build (msrv)"
|
||||
runs-on: depot-ubuntu-latest-8
|
||||
@@ -735,7 +721,7 @@ jobs:
|
||||
- name: "Install Rust toolchain"
|
||||
run: rustup show
|
||||
- name: "Run ty completion evaluation"
|
||||
run: cargo run --release --package ty_completion_eval -- all --threshold 0.4 --tasks /tmp/completion-evaluation-tasks.csv
|
||||
run: cargo run --release --package ty_completion_eval -- all --threshold 0.1 --tasks /tmp/completion-evaluation-tasks.csv
|
||||
- name: "Ensure there are no changes"
|
||||
run: diff ./crates/ty_completion_eval/completion-evaluation-tasks.csv /tmp/completion-evaluation-tasks.csv
|
||||
|
||||
|
||||
70
.github/workflows/sync_typeshed.yaml
vendored
70
.github/workflows/sync_typeshed.yaml
vendored
@@ -16,10 +16,8 @@ name: Sync typeshed
|
||||
# 3. Once the Windows worker is done, a MacOS worker:
|
||||
# a. Checks out the branch created by the Linux worker
|
||||
# b. Syncs all docstrings available on MacOS that are not available on Linux or Windows
|
||||
# c. Attempts to update any snapshots that might have changed
|
||||
# (this sub-step is allowed to fail)
|
||||
# d. Commits the changes and pushes them to the same upstream branch
|
||||
# e. Creates a PR against the `main` branch using the branch all three workers have pushed to
|
||||
# c. Commits the changes and pushes them to the same upstream branch
|
||||
# d. Creates a PR against the `main` branch using the branch all three workers have pushed to
|
||||
# 4. If any of steps 1-3 failed, an issue is created in the `astral-sh/ruff` repository
|
||||
|
||||
on:
|
||||
@@ -29,12 +27,7 @@ on:
|
||||
- cron: "0 0 1,15 * *"
|
||||
|
||||
env:
|
||||
# Don't set this flag globally for the workflow: it does strange things
|
||||
# to the snapshots in the `cargo insta test --accept` step in the MacOS job.
|
||||
#
|
||||
# FORCE_COLOR: 1
|
||||
|
||||
CARGO_TERM_COLOR: always
|
||||
FORCE_COLOR: 1
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
# The name of the upstream branch that the first worker creates,
|
||||
@@ -93,8 +86,6 @@ jobs:
|
||||
git commit -m "Sync typeshed. Source commit: https://github.com/python/typeshed/commit/$(git -C ../typeshed rev-parse HEAD)" --allow-empty
|
||||
- name: Sync Linux docstrings
|
||||
if: ${{ success() }}
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
run: |
|
||||
cd ruff
|
||||
./scripts/codemod_docstrings.sh
|
||||
@@ -134,8 +125,6 @@ jobs:
|
||||
- name: Sync Windows docstrings
|
||||
id: docstrings
|
||||
shell: bash
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
run: ./scripts/codemod_docstrings.sh
|
||||
- name: Commit the changes
|
||||
if: ${{ steps.docstrings.outcome == 'success' }}
|
||||
@@ -172,63 +161,26 @@ jobs:
|
||||
git config --global user.name typeshedbot
|
||||
git config --global user.email '<>'
|
||||
- name: Sync macOS docstrings
|
||||
run: ./scripts/codemod_docstrings.sh
|
||||
- name: Commit and push the changes
|
||||
if: ${{ success() }}
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
run: |
|
||||
./scripts/codemod_docstrings.sh
|
||||
git commit -am "Sync macOS docstrings" --allow-empty
|
||||
- name: Format the changes
|
||||
if: ${{ success() }}
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
run: |
|
||||
|
||||
# Here we just reformat the codemodded stubs so that they are
|
||||
# consistent with the other typeshed stubs around them.
|
||||
# Typeshed formats code using black in their CI, so we just invoke
|
||||
# black on the stubs the same way that typeshed does.
|
||||
uvx black "${VENDORED_TYPESHED}/stdlib" --config "${VENDORED_TYPESHED}/pyproject.toml" || true
|
||||
git commit -am "Format codemodded docstrings" --allow-empty
|
||||
- name: Remove typeshed pyproject.toml file
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
|
||||
rm "${VENDORED_TYPESHED}/pyproject.toml"
|
||||
git commit -am "Remove pyproject.toml file"
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1
|
||||
- name: "Install Rust toolchain"
|
||||
if: ${{ success() }}
|
||||
run: rustup show
|
||||
- name: "Install mold"
|
||||
if: ${{ success() }}
|
||||
uses: rui314/setup-mold@725a8794d15fc7563f59595bd9556495c0564878 # v1
|
||||
- name: "Install cargo nextest"
|
||||
if: ${{ success() }}
|
||||
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- name: "Install cargo insta"
|
||||
if: ${{ success() }}
|
||||
uses: taiki-e/install-action@522492a8c115f1b6d4d318581f09638e9442547b # v2.62.21
|
||||
with:
|
||||
tool: cargo-insta
|
||||
- name: Update snapshots
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
# The `cargo insta` docs indicate that `--unreferenced=delete` might be a good option,
|
||||
# but from local testing it appears to just revert all changes made by `cargo insta test --accept`.
|
||||
#
|
||||
# If there were only snapshot-related failures, `cargo insta test --accept` will have exit code 0,
|
||||
# but if there were also other mdtest failures (for example), it will return a nonzero exit code.
|
||||
# We don't care about other tests failing here, we just want snapshots updated where possible,
|
||||
# so we use `|| true` here to ignore the exit code.
|
||||
cargo insta test --accept --color=always --all-features --test-runner=nextest || true
|
||||
- name: Commit snapshot changes
|
||||
if: ${{ success() }}
|
||||
run: git commit -am "Update snapshots" || echo "No snapshot changes to commit"
|
||||
- name: Push changes upstream and create a PR
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
|
||||
git push
|
||||
- name: Create a PR
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
gh pr list --repo "${GITHUB_REPOSITORY}" --head "${UPSTREAM_BRANCH}" --json id --jq length | grep 1 && exit 0 # exit if there is existing pr
|
||||
gh pr create --title "[ty] Sync vendored typeshed stubs" --body "Close and reopen this PR to trigger CI" --label "ty"
|
||||
|
||||
|
||||
4
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
4
.github/workflows/ty-ecosystem-analyzer.yaml
vendored
@@ -64,12 +64,12 @@ jobs:
|
||||
|
||||
cd ..
|
||||
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@908758da02a73ef3f3308e1dbb2248510029bbe4"
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@279f8a15b0e7f77213bf9096dbc2335a19ef89c5"
|
||||
|
||||
ecosystem-analyzer \
|
||||
--repository ruff \
|
||||
diff \
|
||||
--profile=profiling \
|
||||
--profile=release \
|
||||
--projects-old ruff/projects_old.txt \
|
||||
--projects-new ruff/projects_new.txt \
|
||||
--old old_commit \
|
||||
|
||||
4
.github/workflows/ty-ecosystem-report.yaml
vendored
4
.github/workflows/ty-ecosystem-report.yaml
vendored
@@ -49,13 +49,13 @@ jobs:
|
||||
|
||||
cd ..
|
||||
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@908758da02a73ef3f3308e1dbb2248510029bbe4"
|
||||
uv tool install "git+https://github.com/astral-sh/ecosystem-analyzer@279f8a15b0e7f77213bf9096dbc2335a19ef89c5"
|
||||
|
||||
ecosystem-analyzer \
|
||||
--verbose \
|
||||
--repository ruff \
|
||||
analyze \
|
||||
--profile=profiling \
|
||||
--profile=release \
|
||||
--projects ruff/crates/ty_python_semantic/resources/primer/good.txt \
|
||||
--output ecosystem-diagnostics.json
|
||||
|
||||
|
||||
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -3540,8 +3540,8 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "salsa"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=29ab321b45d00daa4315fa2a06f7207759a8c87e#29ab321b45d00daa4315fa2a06f7207759a8c87e"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"compact_str",
|
||||
@@ -3564,13 +3564,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macro-rules"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=29ab321b45d00daa4315fa2a06f7207759a8c87e#29ab321b45d00daa4315fa2a06f7207759a8c87e"
|
||||
|
||||
[[package]]
|
||||
name = "salsa-macros"
|
||||
version = "0.24.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/salsa-rs/salsa.git?rev=29ab321b45d00daa4315fa2a06f7207759a8c87e#29ab321b45d00daa4315fa2a06f7207759a8c87e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
32
Cargo.toml
32
Cargo.toml
@@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
|
||||
rustc-hash = { version = "2.0.0" }
|
||||
rustc-stable-hash = { version = "0.1.2" }
|
||||
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "ef9f9329be6923acd050c8dddd172e3bc93e8051", default-features = false, features = [
|
||||
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "29ab321b45d00daa4315fa2a06f7207759a8c87e", default-features = false, features = [
|
||||
"compact_str",
|
||||
"macros",
|
||||
"salsa_unstable",
|
||||
@@ -268,7 +268,12 @@ large_stack_arrays = "allow"
|
||||
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
# Note that we set these explicitly, and these values
|
||||
# were chosen based on a trade-off between compile times
|
||||
# and runtime performance[1].
|
||||
#
|
||||
# [1]: https://github.com/astral-sh/ruff/pull/9031
|
||||
lto = "thin"
|
||||
codegen-units = 16
|
||||
|
||||
# Some crates don't change as much but benefit more from
|
||||
@@ -278,8 +283,6 @@ codegen-units = 16
|
||||
codegen-units = 1
|
||||
[profile.release.package.ruff_python_ast]
|
||||
codegen-units = 1
|
||||
[profile.release.package.salsa]
|
||||
codegen-units = 1
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
@@ -295,30 +298,11 @@ opt-level = 3
|
||||
[profile.dev.package.ruff_python_parser]
|
||||
opt-level = 1
|
||||
|
||||
# This profile is meant to mimic the `release` profile as closely as
|
||||
# possible, but using settings that are more beneficial for iterative
|
||||
# development. That is, the `release` profile is intended for actually
|
||||
# building the release, where as `profiling` is meant for building ty/ruff
|
||||
# for running benchmarks.
|
||||
#
|
||||
# The main differences here are to avoid stripping debug information
|
||||
# and disabling fat lto. This does result in a mismatch between our release
|
||||
# configuration and our benchmarking configuration, which is unfortunate.
|
||||
# But compile times with `lto = fat` are completely untenable.
|
||||
#
|
||||
# This setup does risk that we are measuring something in benchmarks
|
||||
# that we aren't shipping, but in order to make those two the same, we'd
|
||||
# either need to make compile times way worse for development, or take
|
||||
# a hit to binary size and a slight hit to runtime performance in our
|
||||
# release builds.
|
||||
#
|
||||
# Use the `--profile profiling` flag to show symbols in release mode.
|
||||
# e.g. `cargo build --profile profiling`
|
||||
[profile.profiling]
|
||||
inherits = "release"
|
||||
strip = false
|
||||
debug = "full"
|
||||
lto = false
|
||||
debug = 1
|
||||
|
||||
# The profile that 'cargo dist' will build with.
|
||||
[profile.dist]
|
||||
|
||||
@@ -28,7 +28,7 @@ An extremely fast Python linter and code formatter, written in Rust.
|
||||
- ⚡️ 10-100x faster than existing linters (like Flake8) and formatters (like Black)
|
||||
- 🐍 Installable via `pip`
|
||||
- 🛠️ `pyproject.toml` support
|
||||
- 🤝 Python 3.14 compatibility
|
||||
- 🤝 Python 3.13 compatibility
|
||||
- ⚖️ Drop-in parity with [Flake8](https://docs.astral.sh/ruff/faq/#how-does-ruffs-linter-compare-to-flake8), isort, and [Black](https://docs.astral.sh/ruff/faq/#how-does-ruffs-formatter-compare-to-black)
|
||||
- 📦 Built-in caching, to avoid re-analyzing unchanged files
|
||||
- 🔧 Fix support, for automatic error correction (e.g., automatically remove unused imports)
|
||||
|
||||
@@ -7,8 +7,7 @@ use ruff_annotate_snippets::Level as AnnotateLevel;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
pub use self::render::{
|
||||
DisplayDiagnostic, DisplayDiagnostics, DummyFileResolver, FileResolver, Input,
|
||||
ceil_char_boundary,
|
||||
DisplayDiagnostic, DisplayDiagnostics, FileResolver, Input, ceil_char_boundary,
|
||||
github::{DisplayGithubDiagnostics, GithubRenderer},
|
||||
};
|
||||
use crate::{Db, files::File};
|
||||
|
||||
@@ -1170,31 +1170,6 @@ pub fn ceil_char_boundary(text: &str, offset: TextSize) -> TextSize {
|
||||
.unwrap_or_else(|| TextSize::from(upper_bound))
|
||||
}
|
||||
|
||||
/// A stub implementation of [`FileResolver`] intended for testing.
|
||||
pub struct DummyFileResolver;
|
||||
|
||||
impl FileResolver for DummyFileResolver {
|
||||
fn path(&self, _file: File) -> &str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn input(&self, _file: File) -> Input {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn notebook_index(&self, _file: &UnifiedFile) -> Option<NotebookIndex> {
|
||||
None
|
||||
}
|
||||
|
||||
fn is_notebook(&self, _file: &UnifiedFile) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn current_directory(&self) -> &Path {
|
||||
Path::new(".")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
@@ -93,39 +93,14 @@ fn generate_markdown() -> String {
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
let status_text = match lint.status() {
|
||||
ty_python_semantic::lint::LintStatus::Stable { since } => {
|
||||
format!(
|
||||
r#"Added in <a href="https://github.com/astral-sh/ty/releases/tag/{since}">{since}</a>"#
|
||||
)
|
||||
}
|
||||
ty_python_semantic::lint::LintStatus::Preview { since } => {
|
||||
format!(
|
||||
r#"Preview (since <a href="https://github.com/astral-sh/ty/releases/tag/{since}">{since}</a>)"#
|
||||
)
|
||||
}
|
||||
ty_python_semantic::lint::LintStatus::Deprecated { since, .. } => {
|
||||
format!(
|
||||
r#"Deprecated (since <a href="https://github.com/astral-sh/ty/releases/tag/{since}">{since}</a>)"#
|
||||
)
|
||||
}
|
||||
ty_python_semantic::lint::LintStatus::Removed { since, .. } => {
|
||||
format!(
|
||||
r#"Removed (since <a href="https://github.com/astral-sh/ty/releases/tag/{since}">{since}</a>)"#
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let _ = writeln!(
|
||||
&mut output,
|
||||
r#"<small>
|
||||
Default level: <a href="../rules.md#rule-levels" title="This lint has a default level of '{level}'."><code>{level}</code></a> ·
|
||||
{status_text} ·
|
||||
<a href="https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20{encoded_name}" target="_blank">Related issues</a> ·
|
||||
<a href="https://github.com/astral-sh/ruff/blob/main/{file}#L{line}" target="_blank">View source</a>
|
||||
Default level: [`{level}`](../rules.md#rule-levels "This lint has a default level of '{level}'.") ·
|
||||
[Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20{encoded_name}) ·
|
||||
[View source](https://github.com/astral-sh/ruff/blob/main/{file}#L{line})
|
||||
</small>
|
||||
|
||||
|
||||
{documentation}
|
||||
"#,
|
||||
level = lint.default_level(),
|
||||
|
||||
@@ -98,10 +98,6 @@ impl Db for ModuleDb {
|
||||
fn lint_registry(&self) -> &LintRegistry {
|
||||
default_lint_registry()
|
||||
}
|
||||
|
||||
fn verbose(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[salsa::db]
|
||||
|
||||
@@ -11,7 +11,7 @@ from airflow import (
|
||||
)
|
||||
from airflow.api_connexion.security import requires_access
|
||||
from airflow.contrib.aws_athena_hook import AWSAthenaHook
|
||||
from airflow.datasets import DatasetAliasEvent, DatasetEvent
|
||||
from airflow.datasets import DatasetAliasEvent
|
||||
from airflow.operators.postgres_operator import Mapping
|
||||
from airflow.operators.subdag import SubDagOperator
|
||||
from airflow.secrets.cache import SecretCache
|
||||
@@ -48,7 +48,6 @@ AWSAthenaHook()
|
||||
|
||||
# airflow.datasets
|
||||
DatasetAliasEvent()
|
||||
DatasetEvent()
|
||||
|
||||
|
||||
# airflow.operators.subdag.*
|
||||
|
||||
@@ -33,10 +33,3 @@ class ShellConfig:
|
||||
|
||||
def run(self, username):
|
||||
Popen("true", shell={**self.shell_defaults, **self.fetch_shell_config(username)})
|
||||
|
||||
# Additional truthiness cases for generator, lambda, and f-strings
|
||||
Popen("true", shell=(i for i in ()))
|
||||
Popen("true", shell=lambda: 0)
|
||||
Popen("true", shell=f"{b''}")
|
||||
x = 1
|
||||
Popen("true", shell=f"{x=}")
|
||||
|
||||
@@ -6,19 +6,3 @@ foo(shell=True)
|
||||
|
||||
foo(shell={**{}})
|
||||
foo(shell={**{**{}}})
|
||||
|
||||
# Truthy non-bool values for `shell`
|
||||
foo(shell=(i for i in ()))
|
||||
foo(shell=lambda: 0)
|
||||
|
||||
# f-strings guaranteed non-empty
|
||||
foo(shell=f"{b''}")
|
||||
x = 1
|
||||
foo(shell=f"{x=}")
|
||||
|
||||
# Additional truthiness cases for generator, lambda, and f-strings
|
||||
foo(shell=(i for i in ()))
|
||||
foo(shell=lambda: 0)
|
||||
foo(shell=f"{b''}")
|
||||
x = 1
|
||||
foo(shell=f"{x=}")
|
||||
|
||||
@@ -9,10 +9,3 @@ os.system("tar cf foo.tar bar/*")
|
||||
|
||||
subprocess.Popen(["chmod", "+w", "*.py"], shell={**{}})
|
||||
subprocess.Popen(["chmod", "+w", "*.py"], shell={**{**{}}})
|
||||
|
||||
# Additional truthiness cases for generator, lambda, and f-strings
|
||||
subprocess.Popen("chmod +w foo*", shell=(i for i in ()))
|
||||
subprocess.Popen("chmod +w foo*", shell=lambda: 0)
|
||||
subprocess.Popen("chmod +w foo*", shell=f"{b''}")
|
||||
x = 1
|
||||
subprocess.Popen("chmod +w foo*", shell=f"{x=}")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# The lexer emits a string token if it's unterminated
|
||||
# The lexer doesn't emit a string token if it's unterminated
|
||||
"a" "b
|
||||
"a" "b" "c
|
||||
"a" """b
|
||||
c""" "d
|
||||
|
||||
# This is also true for
|
||||
# For f-strings, the `FStringRanges` won't contain the range for
|
||||
# unterminated f-strings.
|
||||
f"a" f"b
|
||||
f"a" f"b" f"c
|
||||
|
||||
@@ -78,11 +78,3 @@ b: None | Literal[None] | None
|
||||
c: (None | Literal[None]) | None
|
||||
d: None | (Literal[None] | None)
|
||||
e: None | ((None | Literal[None]) | None) | None
|
||||
|
||||
# Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
@@ -197,10 +197,3 @@ for x in {**a, **b} or [None]:
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/7127
|
||||
def f(a: "'b' or 'c'"): ...
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/20703
|
||||
print(f"{b''}" or "bar") # SIM222
|
||||
x = 1
|
||||
print(f"{x=}" or "bar") # SIM222
|
||||
(lambda: 1) or True # SIM222
|
||||
(i for i in range(1)) or "bar" # SIM222
|
||||
|
||||
@@ -18,20 +18,3 @@ def print_third_word(word: Hello.Text) -> None:
|
||||
|
||||
def print_fourth_word(word: Goodbye) -> None:
|
||||
print(word)
|
||||
|
||||
|
||||
import typing_extensions
|
||||
import typing_extensions as TypingExt
|
||||
from typing_extensions import Text as TextAlias
|
||||
|
||||
|
||||
def print_fifth_word(word: typing_extensions.Text) -> None:
|
||||
print(word)
|
||||
|
||||
|
||||
def print_sixth_word(word: TypingExt.Text) -> None:
|
||||
print(word)
|
||||
|
||||
|
||||
def print_seventh_word(word: TextAlias) -> None:
|
||||
print(word)
|
||||
|
||||
@@ -43,7 +43,7 @@ class Foo:
|
||||
T = typing.TypeVar(*args)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# `default` was added in Python 3.13
|
||||
# `default` should be skipped for now, added in Python 3.13
|
||||
T = typing.TypeVar("T", default=Any)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
@@ -90,9 +90,9 @@ PositiveList = TypeAliasType(
|
||||
"PositiveList2", list[Annotated[T, Gt(0)]], type_params=(T,)
|
||||
)
|
||||
|
||||
# `default` was added in Python 3.13
|
||||
# `default` should be skipped for now, added in Python 3.13
|
||||
T = typing.TypeVar("T", default=Any)
|
||||
AnyList = TypeAliasType("AnyList", list[T], type_params=(T,))
|
||||
AnyList = TypeAliasType("AnyList", list[T], typep_params=(T,))
|
||||
|
||||
# unsafe fix if comments within the fix
|
||||
T = TypeVar("T")
|
||||
@@ -128,7 +128,3 @@ T: TypeAlias = ( # comment0
|
||||
str # comment6
|
||||
# comment7
|
||||
) # comment8
|
||||
|
||||
# Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
T_default = TypeVar("T_default", default=int)
|
||||
DefaultList: TypeAlias = list[T_default]
|
||||
|
||||
@@ -122,7 +122,7 @@ class MixedGenerics[U]:
|
||||
return (u, t)
|
||||
|
||||
|
||||
# default requires 3.13
|
||||
# TODO(brent) default requires 3.13
|
||||
V = TypeVar("V", default=Any, bound=str)
|
||||
|
||||
|
||||
@@ -130,14 +130,6 @@ class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
|
||||
var: V
|
||||
|
||||
|
||||
# Test case for TypeVar with default but no bound
|
||||
W = TypeVar("W", default=int)
|
||||
|
||||
|
||||
class DefaultOnlyTypeVar(Generic[W]): # -> [W = int]
|
||||
var: W
|
||||
|
||||
|
||||
# nested classes and functions are skipped
|
||||
class Outer:
|
||||
class Inner(Generic[T]):
|
||||
|
||||
@@ -44,7 +44,9 @@ def any_str_param(s: AnyStr) -> AnyStr:
|
||||
return s
|
||||
|
||||
|
||||
# default requires 3.13
|
||||
# these cases are not handled
|
||||
|
||||
# TODO(brent) default requires 3.13
|
||||
V = TypeVar("V", default=Any, bound=str)
|
||||
|
||||
|
||||
@@ -52,8 +54,6 @@ def default_var(v: V) -> V:
|
||||
return v
|
||||
|
||||
|
||||
# these cases are not handled
|
||||
|
||||
def outer():
|
||||
def inner(t: T) -> T:
|
||||
return t
|
||||
|
||||
@@ -50,6 +50,24 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::nonlocal_and_global(checker, nonlocal);
|
||||
}
|
||||
}
|
||||
Stmt::Break(_) => {
|
||||
if checker.is_rule_enabled(Rule::BreakOutsideLoop) {
|
||||
pyflakes::rules::break_outside_loop(
|
||||
checker,
|
||||
stmt,
|
||||
&mut checker.semantic.current_statements().skip(1),
|
||||
);
|
||||
}
|
||||
}
|
||||
Stmt::Continue(_) => {
|
||||
if checker.is_rule_enabled(Rule::ContinueOutsideLoop) {
|
||||
pyflakes::rules::continue_outside_loop(
|
||||
checker,
|
||||
stmt,
|
||||
&mut checker.semantic.current_statements().skip(1),
|
||||
);
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(
|
||||
function_def @ ast::StmtFunctionDef {
|
||||
is_async,
|
||||
|
||||
@@ -697,7 +697,6 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::FutureFeatureNotDefined(name) => {
|
||||
// F407
|
||||
if self.is_rule_enabled(Rule::FutureFeatureNotDefined) {
|
||||
self.report_diagnostic(
|
||||
pyflakes::rules::FutureFeatureNotDefined { name },
|
||||
@@ -705,18 +704,6 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::BreakOutsideLoop => {
|
||||
// F701
|
||||
if self.is_rule_enabled(Rule::BreakOutsideLoop) {
|
||||
self.report_diagnostic(pyflakes::rules::BreakOutsideLoop, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ContinueOutsideLoop => {
|
||||
// F702
|
||||
if self.is_rule_enabled(Rule::ContinueOutsideLoop) {
|
||||
self.report_diagnostic(pyflakes::rules::ContinueOutsideLoop, error.range);
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ReboundComprehensionVariable
|
||||
| SemanticSyntaxErrorKind::DuplicateTypeParameter
|
||||
| SemanticSyntaxErrorKind::MultipleCaseAssignment(_)
|
||||
@@ -824,40 +811,19 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn in_loop_context(&self) -> bool {
|
||||
let mut child = self.semantic.current_statement();
|
||||
|
||||
for parent in self.semantic.current_statements().skip(1) {
|
||||
match parent {
|
||||
Stmt::For(ast::StmtFor { orelse, .. })
|
||||
| Stmt::While(ast::StmtWhile { orelse, .. }) => {
|
||||
if !orelse.contains(child) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for Checker<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
// Step 0: Pre-processing
|
||||
self.semantic.push_node(stmt);
|
||||
|
||||
// For functions, defer semantic syntax error checks until the body of the function is
|
||||
// visited
|
||||
if !stmt.is_function_def_stmt() {
|
||||
self.with_semantic_checker(|semantic, context| semantic.visit_stmt(stmt, context));
|
||||
}
|
||||
|
||||
// Step 0: Pre-processing
|
||||
self.semantic.push_node(stmt);
|
||||
|
||||
// For Jupyter Notebooks, we'll reset the `IMPORT_BOUNDARY` flag when
|
||||
// we encounter a cell boundary.
|
||||
if self.source_type.is_ipynb()
|
||||
|
||||
@@ -11,7 +11,8 @@ use crate::settings::types::CompiledPerFileIgnoreList;
|
||||
pub fn get_cwd() -> &'static Path {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
Path::new(".")
|
||||
static CWD: std::sync::LazyLock<PathBuf> = std::sync::LazyLock::new(|| PathBuf::from("."));
|
||||
&CWD
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
path_absolutize::path_dedot::CWD.as_path()
|
||||
|
||||
@@ -242,11 +242,6 @@ pub(crate) const fn is_refined_submodule_import_match_enabled(settings: &LinterS
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/20660
|
||||
pub(crate) const fn is_type_var_default_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// github.com/astral-sh/ruff/issues/20004
|
||||
pub(crate) const fn is_b006_check_guaranteed_mutable_expr_enabled(
|
||||
settings: &LinterSettings,
|
||||
@@ -270,7 +265,3 @@ pub(crate) const fn is_fix_read_whole_file_enabled(settings: &LinterSettings) ->
|
||||
pub(crate) const fn is_fix_write_whole_file_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
pub(crate) const fn is_typing_extensions_str_alias_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
@@ -655,11 +655,6 @@ fn check_name(checker: &Checker, expr: &Expr, range: TextRange) {
|
||||
},
|
||||
// airflow.datasets
|
||||
["airflow", "datasets", "DatasetAliasEvent"] => Replacement::None,
|
||||
["airflow", "datasets", "DatasetEvent"] => Replacement::Message(
|
||||
"`DatasetEvent` has been made private in Airflow 3. \
|
||||
Use `dict[str, Any]` for the time being. \
|
||||
An `AssetEvent` type will be added to the apache-airflow-task-sdk in a future version.",
|
||||
),
|
||||
|
||||
// airflow.hooks
|
||||
["airflow", "hooks", "base_hook", "BaseHook"] => Replacement::Rename {
|
||||
|
||||
@@ -104,49 +104,38 @@ AIR301 `airflow.datasets.DatasetAliasEvent` is removed in Airflow 3.0
|
||||
49 | # airflow.datasets
|
||||
50 | DatasetAliasEvent()
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
51 | DatasetEvent()
|
||||
|
|
||||
|
||||
AIR301 `airflow.datasets.DatasetEvent` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:51:1
|
||||
|
|
||||
49 | # airflow.datasets
|
||||
50 | DatasetAliasEvent()
|
||||
51 | DatasetEvent()
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
help: `DatasetEvent` has been made private in Airflow 3. Use `dict[str, Any]` for the time being. An `AssetEvent` type will be added to the apache-airflow-task-sdk in a future version.
|
||||
|
||||
AIR301 `airflow.operators.subdag.SubDagOperator` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:55:1
|
||||
--> AIR301_names.py:54:1
|
||||
|
|
||||
54 | # airflow.operators.subdag.*
|
||||
55 | SubDagOperator()
|
||||
53 | # airflow.operators.subdag.*
|
||||
54 | SubDagOperator()
|
||||
| ^^^^^^^^^^^^^^
|
||||
56 |
|
||||
57 | # airflow.operators.postgres_operator
|
||||
55 |
|
||||
56 | # airflow.operators.postgres_operator
|
||||
|
|
||||
help: The whole `airflow.subdag` module has been removed.
|
||||
|
||||
AIR301 `airflow.operators.postgres_operator.Mapping` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:58:1
|
||||
--> AIR301_names.py:57:1
|
||||
|
|
||||
57 | # airflow.operators.postgres_operator
|
||||
58 | Mapping()
|
||||
56 | # airflow.operators.postgres_operator
|
||||
57 | Mapping()
|
||||
| ^^^^^^^
|
||||
59 |
|
||||
60 | # airflow.secrets
|
||||
58 |
|
||||
59 | # airflow.secrets
|
||||
|
|
||||
|
||||
AIR301 [*] `airflow.secrets.cache.SecretCache` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:65:1
|
||||
--> AIR301_names.py:64:1
|
||||
|
|
||||
64 | # airflow.secrets.cache
|
||||
65 | SecretCache()
|
||||
63 | # airflow.secrets.cache
|
||||
64 | SecretCache()
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: Use `SecretCache` from `airflow.sdk` instead.
|
||||
14 | from airflow.datasets import DatasetAliasEvent, DatasetEvent
|
||||
14 | from airflow.datasets import DatasetAliasEvent
|
||||
15 | from airflow.operators.postgres_operator import Mapping
|
||||
16 | from airflow.operators.subdag import SubDagOperator
|
||||
- from airflow.secrets.cache import SecretCache
|
||||
@@ -164,211 +153,211 @@ help: Use `SecretCache` from `airflow.sdk` instead.
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
AIR301 `airflow.triggers.external_task.TaskStateTrigger` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:69:1
|
||||
--> AIR301_names.py:68:1
|
||||
|
|
||||
68 | # airflow.triggers.external_task
|
||||
69 | TaskStateTrigger()
|
||||
67 | # airflow.triggers.external_task
|
||||
68 | TaskStateTrigger()
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
70 |
|
||||
71 | # airflow.utils.date
|
||||
69 |
|
||||
70 | # airflow.utils.date
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:72:1
|
||||
--> AIR301_names.py:71:1
|
||||
|
|
||||
71 | # airflow.utils.date
|
||||
72 | dates.date_range
|
||||
70 | # airflow.utils.date
|
||||
71 | dates.date_range
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
73 | dates.days_ago
|
||||
72 | dates.days_ago
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:73:1
|
||||
--> AIR301_names.py:72:1
|
||||
|
|
||||
71 | # airflow.utils.date
|
||||
72 | dates.date_range
|
||||
73 | dates.days_ago
|
||||
70 | # airflow.utils.date
|
||||
71 | dates.date_range
|
||||
72 | dates.days_ago
|
||||
| ^^^^^^^^^^^^^^
|
||||
74 |
|
||||
75 | date_range
|
||||
73 |
|
||||
74 | date_range
|
||||
|
|
||||
help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301 `airflow.utils.dates.date_range` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:75:1
|
||||
--> AIR301_names.py:74:1
|
||||
|
|
||||
73 | dates.days_ago
|
||||
74 |
|
||||
75 | date_range
|
||||
72 | dates.days_ago
|
||||
73 |
|
||||
74 | date_range
|
||||
| ^^^^^^^^^^
|
||||
76 | days_ago
|
||||
77 | infer_time_unit
|
||||
75 | days_ago
|
||||
76 | infer_time_unit
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.days_ago` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:76:1
|
||||
--> AIR301_names.py:75:1
|
||||
|
|
||||
75 | date_range
|
||||
76 | days_ago
|
||||
74 | date_range
|
||||
75 | days_ago
|
||||
| ^^^^^^^^
|
||||
77 | infer_time_unit
|
||||
78 | parse_execution_date
|
||||
76 | infer_time_unit
|
||||
77 | parse_execution_date
|
||||
|
|
||||
help: Use `pendulum.today('UTC').add(days=-N, ...)` instead
|
||||
|
||||
AIR301 `airflow.utils.dates.infer_time_unit` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:77:1
|
||||
--> AIR301_names.py:76:1
|
||||
|
|
||||
75 | date_range
|
||||
76 | days_ago
|
||||
77 | infer_time_unit
|
||||
74 | date_range
|
||||
75 | days_ago
|
||||
76 | infer_time_unit
|
||||
| ^^^^^^^^^^^^^^^
|
||||
78 | parse_execution_date
|
||||
79 | round_time
|
||||
77 | parse_execution_date
|
||||
78 | round_time
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.parse_execution_date` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:78:1
|
||||
--> AIR301_names.py:77:1
|
||||
|
|
||||
76 | days_ago
|
||||
77 | infer_time_unit
|
||||
78 | parse_execution_date
|
||||
75 | days_ago
|
||||
76 | infer_time_unit
|
||||
77 | parse_execution_date
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
79 | round_time
|
||||
80 | scale_time_units
|
||||
78 | round_time
|
||||
79 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.round_time` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:79:1
|
||||
--> AIR301_names.py:78:1
|
||||
|
|
||||
77 | infer_time_unit
|
||||
78 | parse_execution_date
|
||||
79 | round_time
|
||||
76 | infer_time_unit
|
||||
77 | parse_execution_date
|
||||
78 | round_time
|
||||
| ^^^^^^^^^^
|
||||
80 | scale_time_units
|
||||
79 | scale_time_units
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dates.scale_time_units` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:80:1
|
||||
--> AIR301_names.py:79:1
|
||||
|
|
||||
78 | parse_execution_date
|
||||
79 | round_time
|
||||
80 | scale_time_units
|
||||
77 | parse_execution_date
|
||||
78 | round_time
|
||||
79 | scale_time_units
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
81 |
|
||||
82 | # This one was not deprecated.
|
||||
80 |
|
||||
81 | # This one was not deprecated.
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.dag_cycle_tester.test_cycle` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:87:1
|
||||
--> AIR301_names.py:86:1
|
||||
|
|
||||
86 | # airflow.utils.dag_cycle_tester
|
||||
87 | test_cycle
|
||||
85 | # airflow.utils.dag_cycle_tester
|
||||
86 | test_cycle
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.db.create_session` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:91:1
|
||||
--> AIR301_names.py:90:1
|
||||
|
|
||||
90 | # airflow.utils.db
|
||||
91 | create_session
|
||||
89 | # airflow.utils.db
|
||||
90 | create_session
|
||||
| ^^^^^^^^^^^^^^
|
||||
92 |
|
||||
93 | # airflow.utils.decorators
|
||||
91 |
|
||||
92 | # airflow.utils.decorators
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.decorators.apply_defaults` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:94:1
|
||||
--> AIR301_names.py:93:1
|
||||
|
|
||||
93 | # airflow.utils.decorators
|
||||
94 | apply_defaults
|
||||
92 | # airflow.utils.decorators
|
||||
93 | apply_defaults
|
||||
| ^^^^^^^^^^^^^^
|
||||
95 |
|
||||
96 | # airflow.utils.file
|
||||
94 |
|
||||
95 | # airflow.utils.file
|
||||
|
|
||||
help: `apply_defaults` is now unconditionally done and can be safely removed.
|
||||
|
||||
AIR301 `airflow.utils.file.mkdirs` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:97:1
|
||||
--> AIR301_names.py:96:1
|
||||
|
|
||||
96 | # airflow.utils.file
|
||||
97 | mkdirs
|
||||
95 | # airflow.utils.file
|
||||
96 | mkdirs
|
||||
| ^^^^^^
|
||||
|
|
||||
help: Use `pathlib.Path({path}).mkdir` instead
|
||||
|
||||
AIR301 `airflow.utils.state.SHUTDOWN` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:101:1
|
||||
--> AIR301_names.py:100:1
|
||||
|
|
||||
100 | # airflow.utils.state
|
||||
101 | SHUTDOWN
|
||||
99 | # airflow.utils.state
|
||||
100 | SHUTDOWN
|
||||
| ^^^^^^^^
|
||||
102 | terminating_states
|
||||
101 | terminating_states
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.state.terminating_states` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:102:1
|
||||
--> AIR301_names.py:101:1
|
||||
|
|
||||
100 | # airflow.utils.state
|
||||
101 | SHUTDOWN
|
||||
102 | terminating_states
|
||||
99 | # airflow.utils.state
|
||||
100 | SHUTDOWN
|
||||
101 | terminating_states
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
103 |
|
||||
104 | # airflow.utils.trigger_rule
|
||||
102 |
|
||||
103 | # airflow.utils.trigger_rule
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.trigger_rule.TriggerRule.DUMMY` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:105:1
|
||||
--> AIR301_names.py:104:1
|
||||
|
|
||||
104 | # airflow.utils.trigger_rule
|
||||
105 | TriggerRule.DUMMY
|
||||
103 | # airflow.utils.trigger_rule
|
||||
104 | TriggerRule.DUMMY
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
106 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
105 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
|
|
||||
|
||||
AIR301 `airflow.utils.trigger_rule.TriggerRule.NONE_FAILED_OR_SKIPPED` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:106:1
|
||||
--> AIR301_names.py:105:1
|
||||
|
|
||||
104 | # airflow.utils.trigger_rule
|
||||
105 | TriggerRule.DUMMY
|
||||
106 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
103 | # airflow.utils.trigger_rule
|
||||
104 | TriggerRule.DUMMY
|
||||
105 | TriggerRule.NONE_FAILED_OR_SKIPPED
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.auth.has_access` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:110:1
|
||||
--> AIR301_names.py:109:1
|
||||
|
|
||||
109 | # airflow.www.auth
|
||||
110 | has_access
|
||||
108 | # airflow.www.auth
|
||||
109 | has_access
|
||||
| ^^^^^^^^^^
|
||||
111 | has_access_dataset
|
||||
110 | has_access_dataset
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.auth.has_access_dataset` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:111:1
|
||||
--> AIR301_names.py:110:1
|
||||
|
|
||||
109 | # airflow.www.auth
|
||||
110 | has_access
|
||||
111 | has_access_dataset
|
||||
108 | # airflow.www.auth
|
||||
109 | has_access
|
||||
110 | has_access_dataset
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
112 |
|
||||
113 | # airflow.www.utils
|
||||
111 |
|
||||
112 | # airflow.www.utils
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.utils.get_sensitive_variables_fields` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:114:1
|
||||
--> AIR301_names.py:113:1
|
||||
|
|
||||
113 | # airflow.www.utils
|
||||
114 | get_sensitive_variables_fields
|
||||
112 | # airflow.www.utils
|
||||
113 | get_sensitive_variables_fields
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
115 | should_hide_value_for_key
|
||||
114 | should_hide_value_for_key
|
||||
|
|
||||
|
||||
AIR301 `airflow.www.utils.should_hide_value_for_key` is removed in Airflow 3.0
|
||||
--> AIR301_names.py:115:1
|
||||
--> AIR301_names.py:114:1
|
||||
|
|
||||
113 | # airflow.www.utils
|
||||
114 | get_sensitive_variables_fields
|
||||
115 | should_hide_value_for_key
|
||||
112 | # airflow.www.utils
|
||||
113 | get_sensitive_variables_fields
|
||||
114 | should_hide_value_for_key
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
@@ -127,44 +127,3 @@ S602 `subprocess` call with `shell=True` identified, security issue
|
||||
21 |
|
||||
22 | # Check dict display with only double-starred expressions can be falsey.
|
||||
|
|
||||
|
||||
S602 `subprocess` call with truthy `shell` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||
--> S602.py:38:1
|
||||
|
|
||||
37 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
38 | Popen("true", shell=(i for i in ()))
|
||||
| ^^^^^
|
||||
39 | Popen("true", shell=lambda: 0)
|
||||
40 | Popen("true", shell=f"{b''}")
|
||||
|
|
||||
|
||||
S602 `subprocess` call with truthy `shell` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||
--> S602.py:39:1
|
||||
|
|
||||
37 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
38 | Popen("true", shell=(i for i in ()))
|
||||
39 | Popen("true", shell=lambda: 0)
|
||||
| ^^^^^
|
||||
40 | Popen("true", shell=f"{b''}")
|
||||
41 | x = 1
|
||||
|
|
||||
|
||||
S602 `subprocess` call with truthy `shell` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||
--> S602.py:40:1
|
||||
|
|
||||
38 | Popen("true", shell=(i for i in ()))
|
||||
39 | Popen("true", shell=lambda: 0)
|
||||
40 | Popen("true", shell=f"{b''}")
|
||||
| ^^^^^
|
||||
41 | x = 1
|
||||
42 | Popen("true", shell=f"{x=}")
|
||||
|
|
||||
|
||||
S602 `subprocess` call with truthy `shell` seems safe, but may be changed in the future; consider rewriting without `shell`
|
||||
--> S602.py:42:1
|
||||
|
|
||||
40 | Popen("true", shell=f"{b''}")
|
||||
41 | x = 1
|
||||
42 | Popen("true", shell=f"{x=}")
|
||||
| ^^^^^
|
||||
|
|
||||
|
||||
@@ -9,85 +9,3 @@ S604 Function call with `shell=True` parameter identified, security issue
|
||||
6 |
|
||||
7 | foo(shell={**{}})
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:11:1
|
||||
|
|
||||
10 | # Truthy non-bool values for `shell`
|
||||
11 | foo(shell=(i for i in ()))
|
||||
| ^^^
|
||||
12 | foo(shell=lambda: 0)
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:12:1
|
||||
|
|
||||
10 | # Truthy non-bool values for `shell`
|
||||
11 | foo(shell=(i for i in ()))
|
||||
12 | foo(shell=lambda: 0)
|
||||
| ^^^
|
||||
13 |
|
||||
14 | # f-strings guaranteed non-empty
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:15:1
|
||||
|
|
||||
14 | # f-strings guaranteed non-empty
|
||||
15 | foo(shell=f"{b''}")
|
||||
| ^^^
|
||||
16 | x = 1
|
||||
17 | foo(shell=f"{x=}")
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:17:1
|
||||
|
|
||||
15 | foo(shell=f"{b''}")
|
||||
16 | x = 1
|
||||
17 | foo(shell=f"{x=}")
|
||||
| ^^^
|
||||
18 |
|
||||
19 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:20:1
|
||||
|
|
||||
19 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
20 | foo(shell=(i for i in ()))
|
||||
| ^^^
|
||||
21 | foo(shell=lambda: 0)
|
||||
22 | foo(shell=f"{b''}")
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:21:1
|
||||
|
|
||||
19 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
20 | foo(shell=(i for i in ()))
|
||||
21 | foo(shell=lambda: 0)
|
||||
| ^^^
|
||||
22 | foo(shell=f"{b''}")
|
||||
23 | x = 1
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:22:1
|
||||
|
|
||||
20 | foo(shell=(i for i in ()))
|
||||
21 | foo(shell=lambda: 0)
|
||||
22 | foo(shell=f"{b''}")
|
||||
| ^^^
|
||||
23 | x = 1
|
||||
24 | foo(shell=f"{x=}")
|
||||
|
|
||||
|
||||
S604 Function call with truthy `shell` parameter identified, security issue
|
||||
--> S604.py:24:1
|
||||
|
|
||||
22 | foo(shell=f"{b''}")
|
||||
23 | x = 1
|
||||
24 | foo(shell=f"{x=}")
|
||||
| ^^^
|
||||
|
|
||||
|
||||
@@ -43,44 +43,3 @@ S609 Possible wildcard injection in call due to `*` usage
|
||||
9 |
|
||||
10 | subprocess.Popen(["chmod", "+w", "*.py"], shell={**{}})
|
||||
|
|
||||
|
||||
S609 Possible wildcard injection in call due to `*` usage
|
||||
--> S609.py:14:18
|
||||
|
|
||||
13 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
14 | subprocess.Popen("chmod +w foo*", shell=(i for i in ()))
|
||||
| ^^^^^^^^^^^^^^^
|
||||
15 | subprocess.Popen("chmod +w foo*", shell=lambda: 0)
|
||||
16 | subprocess.Popen("chmod +w foo*", shell=f"{b''}")
|
||||
|
|
||||
|
||||
S609 Possible wildcard injection in call due to `*` usage
|
||||
--> S609.py:15:18
|
||||
|
|
||||
13 | # Additional truthiness cases for generator, lambda, and f-strings
|
||||
14 | subprocess.Popen("chmod +w foo*", shell=(i for i in ()))
|
||||
15 | subprocess.Popen("chmod +w foo*", shell=lambda: 0)
|
||||
| ^^^^^^^^^^^^^^^
|
||||
16 | subprocess.Popen("chmod +w foo*", shell=f"{b''}")
|
||||
17 | x = 1
|
||||
|
|
||||
|
||||
S609 Possible wildcard injection in call due to `*` usage
|
||||
--> S609.py:16:18
|
||||
|
|
||||
14 | subprocess.Popen("chmod +w foo*", shell=(i for i in ()))
|
||||
15 | subprocess.Popen("chmod +w foo*", shell=lambda: 0)
|
||||
16 | subprocess.Popen("chmod +w foo*", shell=f"{b''}")
|
||||
| ^^^^^^^^^^^^^^^
|
||||
17 | x = 1
|
||||
18 | subprocess.Popen("chmod +w foo*", shell=f"{x=}")
|
||||
|
|
||||
|
||||
S609 Possible wildcard injection in call due to `*` usage
|
||||
--> S609.py:18:18
|
||||
|
|
||||
16 | subprocess.Popen("chmod +w foo*", shell=f"{b''}")
|
||||
17 | x = 1
|
||||
18 | subprocess.Popen("chmod +w foo*", shell=f"{x=}")
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
|
||||
@@ -188,10 +188,16 @@ fn move_initialization(
|
||||
content.push_str(stylist.line_ending().as_str());
|
||||
content.push_str(stylist.indentation());
|
||||
if is_b006_unsafe_fix_preserve_assignment_expr_enabled(checker.settings()) {
|
||||
let annotation = if let Some(ann) = parameter.annotation() {
|
||||
format!(": {}", locator.slice(ann))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let _ = write!(
|
||||
&mut content,
|
||||
"{} = {}",
|
||||
"{}{} = {}",
|
||||
parameter.parameter.name(),
|
||||
annotation,
|
||||
locator.slice(
|
||||
parenthesized_range(
|
||||
default.into(),
|
||||
|
||||
@@ -16,7 +16,7 @@ help: Replace with `None`; initialize within function
|
||||
5 + def import_module_wrong(value: dict[str, str] = None):
|
||||
6 | import os
|
||||
7 + if value is None:
|
||||
8 + value = {}
|
||||
8 + value: dict[str, str] = {}
|
||||
9 |
|
||||
10 |
|
||||
11 | def import_module_with_values_wrong(value: dict[str, str] = {}):
|
||||
@@ -38,7 +38,7 @@ help: Replace with `None`; initialize within function
|
||||
10 | import os
|
||||
11 |
|
||||
12 + if value is None:
|
||||
13 + value = {}
|
||||
13 + value: dict[str, str] = {}
|
||||
14 | return 2
|
||||
15 |
|
||||
16 |
|
||||
@@ -62,7 +62,7 @@ help: Replace with `None`; initialize within function
|
||||
17 | import sys
|
||||
18 | import itertools
|
||||
19 + if value is None:
|
||||
20 + value = {}
|
||||
20 + value: dict[str, str] = {}
|
||||
21 |
|
||||
22 |
|
||||
23 | def from_import_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -83,7 +83,7 @@ help: Replace with `None`; initialize within function
|
||||
21 + def from_import_module_wrong(value: dict[str, str] = None):
|
||||
22 | from os import path
|
||||
23 + if value is None:
|
||||
24 + value = {}
|
||||
24 + value: dict[str, str] = {}
|
||||
25 |
|
||||
26 |
|
||||
27 | def from_imports_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -106,7 +106,7 @@ help: Replace with `None`; initialize within function
|
||||
26 | from os import path
|
||||
27 | from sys import version_info
|
||||
28 + if value is None:
|
||||
29 + value = {}
|
||||
29 + value: dict[str, str] = {}
|
||||
30 |
|
||||
31 |
|
||||
32 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -129,7 +129,7 @@ help: Replace with `None`; initialize within function
|
||||
31 | import os
|
||||
32 | from sys import version_info
|
||||
33 + if value is None:
|
||||
34 + value = {}
|
||||
34 + value: dict[str, str] = {}
|
||||
35 |
|
||||
36 |
|
||||
37 | def import_docstring_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -152,7 +152,7 @@ help: Replace with `None`; initialize within function
|
||||
36 | """Docstring"""
|
||||
37 | import os
|
||||
38 + if value is None:
|
||||
39 + value = {}
|
||||
39 + value: dict[str, str] = {}
|
||||
40 |
|
||||
41 |
|
||||
42 | def import_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -175,7 +175,7 @@ help: Replace with `None`; initialize within function
|
||||
41 | """Docstring"""
|
||||
42 | import os; import sys
|
||||
43 + if value is None:
|
||||
44 + value = {}
|
||||
44 + value: dict[str, str] = {}
|
||||
45 |
|
||||
46 |
|
||||
47 | def import_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -197,7 +197,7 @@ help: Replace with `None`; initialize within function
|
||||
45 + def import_module_wrong(value: dict[str, str] = None):
|
||||
46 | """Docstring"""
|
||||
47 + if value is None:
|
||||
48 + value = {}
|
||||
48 + value: dict[str, str] = {}
|
||||
49 | import os; import sys; x = 1
|
||||
50 |
|
||||
51 |
|
||||
@@ -220,7 +220,7 @@ help: Replace with `None`; initialize within function
|
||||
51 | """Docstring"""
|
||||
52 | import os; import sys
|
||||
53 + if value is None:
|
||||
54 + value = {}
|
||||
54 + value: dict[str, str] = {}
|
||||
55 |
|
||||
56 |
|
||||
57 | def import_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -241,7 +241,7 @@ help: Replace with `None`; initialize within function
|
||||
55 + def import_module_wrong(value: dict[str, str] = None):
|
||||
56 | import os; import sys
|
||||
57 + if value is None:
|
||||
58 + value = {}
|
||||
58 + value: dict[str, str] = {}
|
||||
59 |
|
||||
60 |
|
||||
61 | def import_module_wrong(value: dict[str, str] = {}):
|
||||
@@ -261,7 +261,7 @@ help: Replace with `None`; initialize within function
|
||||
- def import_module_wrong(value: dict[str, str] = {}):
|
||||
59 + def import_module_wrong(value: dict[str, str] = None):
|
||||
60 + if value is None:
|
||||
61 + value = {}
|
||||
61 + value: dict[str, str] = {}
|
||||
62 | import os; import sys; x = 1
|
||||
63 |
|
||||
64 |
|
||||
@@ -282,7 +282,7 @@ help: Replace with `None`; initialize within function
|
||||
63 + def import_module_wrong(value: dict[str, str] = None):
|
||||
64 | import os; import sys
|
||||
65 + if value is None:
|
||||
66 + value = {}
|
||||
66 + value: dict[str, str] = {}
|
||||
67 |
|
||||
68 |
|
||||
69 | def import_module_wrong(value: dict[str, str] = {}): import os
|
||||
|
||||
@@ -51,7 +51,7 @@ help: Replace with `None`; initialize within function
|
||||
10 + def baz(a: list = None):
|
||||
11 | """This one raises a different exception"""
|
||||
12 + if a is None:
|
||||
13 + a = []
|
||||
13 + a: list = []
|
||||
14 | raise IndexError()
|
||||
15 |
|
||||
16 |
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_implicit_str_concat/mod.rs
|
||||
---
|
||||
ISC001 Implicitly concatenated string literals on one line
|
||||
--> ISC_syntax_error.py:2:1
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
| ^^^^^^
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
|
|
||||
help: Combine string literals
|
||||
|
||||
invalid-syntax: missing closing quote in string literal
|
||||
--> ISC_syntax_error.py:2:5
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
| ^^
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
|
|
||||
|
||||
invalid-syntax: Expected a statement
|
||||
--> ISC_syntax_error.py:2:7
|
||||
|
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
| ^
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
|
|
||||
|
||||
ISC001 Implicitly concatenated string literals on one line
|
||||
--> ISC_syntax_error.py:3:1
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
| ^^^^^^^
|
||||
@@ -34,22 +33,10 @@ ISC001 Implicitly concatenated string literals on one line
|
||||
|
|
||||
help: Combine string literals
|
||||
|
||||
ISC001 Implicitly concatenated string literals on one line
|
||||
--> ISC_syntax_error.py:3:5
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
| ^^^^^^
|
||||
4 | "a" """b
|
||||
5 | c""" "d
|
||||
|
|
||||
help: Combine string literals
|
||||
|
||||
invalid-syntax: missing closing quote in string literal
|
||||
--> ISC_syntax_error.py:3:9
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
| ^^
|
||||
@@ -57,6 +44,17 @@ invalid-syntax: missing closing quote in string literal
|
||||
5 | c""" "d
|
||||
|
|
||||
|
||||
invalid-syntax: Expected a statement
|
||||
--> ISC_syntax_error.py:3:11
|
||||
|
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
| ^
|
||||
4 | "a" """b
|
||||
5 | c""" "d
|
||||
|
|
||||
|
||||
ISC001 Implicitly concatenated string literals on one line
|
||||
--> ISC_syntax_error.py:4:1
|
||||
|
|
||||
@@ -66,21 +64,7 @@ ISC001 Implicitly concatenated string literals on one line
|
||||
5 | | c""" "d
|
||||
| |____^
|
||||
6 |
|
||||
7 | # This is also true for
|
||||
|
|
||||
help: Combine string literals
|
||||
|
||||
ISC001 Implicitly concatenated string literals on one line
|
||||
--> ISC_syntax_error.py:4:5
|
||||
|
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
| _____^
|
||||
5 | | c""" "d
|
||||
| |_______^
|
||||
6 |
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
|
|
||||
help: Combine string literals
|
||||
|
||||
@@ -92,13 +76,24 @@ invalid-syntax: missing closing quote in string literal
|
||||
5 | c""" "d
|
||||
| ^^
|
||||
6 |
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
|
|
||||
|
||||
invalid-syntax: Expected a statement
|
||||
--> ISC_syntax_error.py:5:8
|
||||
|
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
5 | c""" "d
|
||||
| ^
|
||||
6 |
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
|
|
||||
|
||||
invalid-syntax: f-string: unterminated string
|
||||
--> ISC_syntax_error.py:9:8
|
||||
|
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
8 | # unterminated f-strings.
|
||||
9 | f"a" f"b
|
||||
| ^
|
||||
@@ -109,7 +104,7 @@ invalid-syntax: f-string: unterminated string
|
||||
invalid-syntax: Expected FStringEnd, found newline
|
||||
--> ISC_syntax_error.py:9:9
|
||||
|
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
8 | # unterminated f-strings.
|
||||
9 | f"a" f"b
|
||||
| ^
|
||||
@@ -188,6 +183,14 @@ invalid-syntax: f-string: unterminated triple-quoted string
|
||||
| |__^
|
||||
|
|
||||
|
||||
invalid-syntax: unexpected EOF while parsing
|
||||
--> ISC_syntax_error.py:30:1
|
||||
|
|
||||
28 | "i" "j"
|
||||
29 | )
|
||||
| ^
|
||||
|
|
||||
|
||||
invalid-syntax: f-string: unterminated string
|
||||
--> ISC_syntax_error.py:30:1
|
||||
|
|
||||
|
||||
@@ -4,17 +4,27 @@ source: crates/ruff_linter/src/rules/flake8_implicit_str_concat/mod.rs
|
||||
invalid-syntax: missing closing quote in string literal
|
||||
--> ISC_syntax_error.py:2:5
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
| ^^
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
|
|
||||
|
||||
invalid-syntax: Expected a statement
|
||||
--> ISC_syntax_error.py:2:7
|
||||
|
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
| ^
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
|
|
||||
|
||||
invalid-syntax: missing closing quote in string literal
|
||||
--> ISC_syntax_error.py:3:9
|
||||
|
|
||||
1 | # The lexer emits a string token if it's unterminated
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
| ^^
|
||||
@@ -22,6 +32,17 @@ invalid-syntax: missing closing quote in string literal
|
||||
5 | c""" "d
|
||||
|
|
||||
|
||||
invalid-syntax: Expected a statement
|
||||
--> ISC_syntax_error.py:3:11
|
||||
|
|
||||
1 | # The lexer doesn't emit a string token if it's unterminated
|
||||
2 | "a" "b
|
||||
3 | "a" "b" "c
|
||||
| ^
|
||||
4 | "a" """b
|
||||
5 | c""" "d
|
||||
|
|
||||
|
||||
invalid-syntax: missing closing quote in string literal
|
||||
--> ISC_syntax_error.py:5:6
|
||||
|
|
||||
@@ -30,13 +51,24 @@ invalid-syntax: missing closing quote in string literal
|
||||
5 | c""" "d
|
||||
| ^^
|
||||
6 |
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
|
|
||||
|
||||
invalid-syntax: Expected a statement
|
||||
--> ISC_syntax_error.py:5:8
|
||||
|
|
||||
3 | "a" "b" "c
|
||||
4 | "a" """b
|
||||
5 | c""" "d
|
||||
| ^
|
||||
6 |
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
|
|
||||
|
||||
invalid-syntax: f-string: unterminated string
|
||||
--> ISC_syntax_error.py:9:8
|
||||
|
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
8 | # unterminated f-strings.
|
||||
9 | f"a" f"b
|
||||
| ^
|
||||
@@ -47,7 +79,7 @@ invalid-syntax: f-string: unterminated string
|
||||
invalid-syntax: Expected FStringEnd, found newline
|
||||
--> ISC_syntax_error.py:9:9
|
||||
|
|
||||
7 | # This is also true for
|
||||
7 | # For f-strings, the `FStringRanges` won't contain the range for
|
||||
8 | # unterminated f-strings.
|
||||
9 | f"a" f"b
|
||||
| ^
|
||||
@@ -101,6 +133,14 @@ invalid-syntax: f-string: unterminated triple-quoted string
|
||||
| |__^
|
||||
|
|
||||
|
||||
invalid-syntax: unexpected EOF while parsing
|
||||
--> ISC_syntax_error.py:30:1
|
||||
|
|
||||
28 | "i" "j"
|
||||
29 | )
|
||||
| ^
|
||||
|
|
||||
|
||||
invalid-syntax: f-string: unterminated string
|
||||
--> ISC_syntax_error.py:30:1
|
||||
|
|
||||
|
||||
@@ -4,8 +4,6 @@ use ruff_python_ast::{
|
||||
self as ast, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, Operator, PythonVersion,
|
||||
helpers::{pep_604_union, typing_optional},
|
||||
name::Name,
|
||||
operator_precedence::OperatorPrecedence,
|
||||
parenthesize::parenthesized_range,
|
||||
};
|
||||
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
@@ -240,19 +238,7 @@ fn create_fix(
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
});
|
||||
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
|
||||
|
||||
// Check if we need parentheses to preserve operator precedence
|
||||
let content = if needs_parentheses_for_precedence(
|
||||
semantic,
|
||||
literal_expr,
|
||||
checker.comment_ranges(),
|
||||
checker.source(),
|
||||
) {
|
||||
format!("({})", checker.generator().expr(&union_expr))
|
||||
} else {
|
||||
checker.generator().expr(&union_expr)
|
||||
};
|
||||
|
||||
let content = checker.generator().expr(&union_expr);
|
||||
let union_edit = Edit::range_replacement(content, literal_expr.range());
|
||||
Fix::applicable_edit(union_edit, applicability)
|
||||
}
|
||||
@@ -270,37 +256,3 @@ enum UnionKind {
|
||||
TypingOptional,
|
||||
BitOr,
|
||||
}
|
||||
|
||||
/// Check if the union expression needs parentheses to preserve operator precedence.
|
||||
/// This is needed when the union is part of a larger expression where the `|` operator
|
||||
/// has lower precedence than the surrounding operations (like attribute access).
|
||||
fn needs_parentheses_for_precedence(
|
||||
semantic: &ruff_python_semantic::SemanticModel,
|
||||
literal_expr: &Expr,
|
||||
comment_ranges: &ruff_python_trivia::CommentRanges,
|
||||
source: &str,
|
||||
) -> bool {
|
||||
// Get the parent expression to check if we're in a context that needs parentheses
|
||||
let Some(parent_expr) = semantic.current_expression_parent() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Check if the literal expression is already parenthesized
|
||||
if parenthesized_range(
|
||||
literal_expr.into(),
|
||||
parent_expr.into(),
|
||||
comment_ranges,
|
||||
source,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return false; // Already parenthesized, don't add more
|
||||
}
|
||||
|
||||
// Check if the parent expression has higher precedence than the `|` operator
|
||||
let union_precedence = OperatorPrecedence::BitOr;
|
||||
let parent_precedence = OperatorPrecedence::from(parent_expr);
|
||||
|
||||
// If the parent operation has higher precedence than `|`, we need parentheses
|
||||
parent_precedence > union_precedence
|
||||
}
|
||||
|
||||
@@ -423,117 +423,5 @@ PYI061 Use `None` rather than `Literal[None]`
|
||||
79 | d: None | (Literal[None] | None)
|
||||
80 | e: None | ((None | Literal[None]) | None) | None
|
||||
| ^^^^
|
||||
81 |
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
|
|
||||
help: Replace with `None`
|
||||
|
||||
PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:83:18
|
||||
|
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
| ^^^^
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
|
|
||||
help: Replace with `Literal[...] | None`
|
||||
80 | e: None | ((None | Literal[None]) | None) | None
|
||||
81 |
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
- print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
83 + print((Literal[1] | None).__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
|
||||
PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:84:18
|
||||
|
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
| ^^^^
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
|
|
||||
help: Replace with `Literal[...] | None`
|
||||
81 |
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
- print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
84 + print((Literal[1] | None).method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
|
||||
PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:85:18
|
||||
|
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
| ^^^^
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
|
|
||||
help: Replace with `Literal[...] | None`
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
- print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
85 + print((Literal[1] | None)[0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:86:18
|
||||
|
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
| ^^^^
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
|
||||
help: Replace with `Literal[...] | None`
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
- print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
86 + print((Literal[1] | None) + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:87:18
|
||||
|
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
| ^^^^
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
|
||||
help: Replace with `Literal[...] | None`
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
- print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
87 + print((Literal[1] | None) * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:88:19
|
||||
|
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
| ^^^^
|
||||
|
|
||||
help: Replace with `Literal[...] | None`
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
- print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
88 + print((Literal[1] | None).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
@@ -465,153 +465,5 @@ PYI061 Use `None` rather than `Literal[None]`
|
||||
79 | d: None | (Literal[None] | None)
|
||||
80 | e: None | ((None | Literal[None]) | None) | None
|
||||
| ^^^^
|
||||
81 |
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
|
|
||||
help: Replace with `None`
|
||||
|
||||
PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:83:18
|
||||
|
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
| ^^^^
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
|
|
||||
help: Replace with `Optional[Literal[...]]`
|
||||
- from typing import Literal, Union
|
||||
1 + from typing import Literal, Union, Optional
|
||||
2 |
|
||||
3 |
|
||||
4 | def func1(arg1: Literal[None]):
|
||||
--------------------------------------------------------------------------------
|
||||
80 | e: None | ((None | Literal[None]) | None) | None
|
||||
81 |
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
- print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
83 + print(Optional[Literal[1]].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
|
||||
PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:84:18
|
||||
|
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
| ^^^^
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
|
|
||||
help: Replace with `Optional[Literal[...]]`
|
||||
- from typing import Literal, Union
|
||||
1 + from typing import Literal, Union, Optional
|
||||
2 |
|
||||
3 |
|
||||
4 | def func1(arg1: Literal[None]):
|
||||
--------------------------------------------------------------------------------
|
||||
81 |
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
- print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
84 + print(Optional[Literal[1]].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
|
||||
PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:85:18
|
||||
|
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
| ^^^^
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
|
|
||||
help: Replace with `Optional[Literal[...]]`
|
||||
- from typing import Literal, Union
|
||||
1 + from typing import Literal, Union, Optional
|
||||
2 |
|
||||
3 |
|
||||
4 | def func1(arg1: Literal[None]):
|
||||
--------------------------------------------------------------------------------
|
||||
82 | # Test cases for operator precedence issue (https://github.com/astral-sh/ruff/issues/20265)
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
- print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
85 + print(Optional[Literal[1]][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:86:18
|
||||
|
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
| ^^^^
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
|
||||
help: Replace with `Optional[Literal[...]]`
|
||||
- from typing import Literal, Union
|
||||
1 + from typing import Literal, Union, Optional
|
||||
2 |
|
||||
3 |
|
||||
4 | def func1(arg1: Literal[None]):
|
||||
--------------------------------------------------------------------------------
|
||||
83 | print(Literal[1, None].__dict__) # Should become (Literal[1] | None).__dict__
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
- print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
86 + print(Optional[Literal[1]] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:87:18
|
||||
|
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
| ^^^^
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
|
||||
help: Replace with `Optional[Literal[...]]`
|
||||
- from typing import Literal, Union
|
||||
1 + from typing import Literal, Union, Optional
|
||||
2 |
|
||||
3 |
|
||||
4 | def func1(arg1: Literal[None]):
|
||||
--------------------------------------------------------------------------------
|
||||
84 | print(Literal[1, None].method()) # Should become (Literal[1] | None).method()
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
- print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
87 + print(Optional[Literal[1]] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
||||
--> PYI061.py:88:19
|
||||
|
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
88 | print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
| ^^^^
|
||||
|
|
||||
help: Replace with `Optional[Literal[...]]`
|
||||
- from typing import Literal, Union
|
||||
1 + from typing import Literal, Union, Optional
|
||||
2 |
|
||||
3 |
|
||||
4 | def func1(arg1: Literal[None]):
|
||||
--------------------------------------------------------------------------------
|
||||
85 | print(Literal[1, None][0]) # Should become (Literal[1] | None)[0]
|
||||
86 | print(Literal[1, None] + 1) # Should become (Literal[1] | None) + 1
|
||||
87 | print(Literal[1, None] * 2) # Should become (Literal[1] | None) * 2
|
||||
- print((Literal[1, None]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
88 + print((Optional[Literal[1]]).__dict__) # Should become ((Literal[1] | None)).__dict__
|
||||
|
||||
@@ -1062,77 +1062,3 @@ help: Replace with `"bar"`
|
||||
170 |
|
||||
171 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM222 [*] Use `f"{b''}"` instead of `f"{b''}" or ...`
|
||||
--> SIM222.py:202:7
|
||||
|
|
||||
201 | # https://github.com/astral-sh/ruff/issues/20703
|
||||
202 | print(f"{b''}" or "bar") # SIM222
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
203 | x = 1
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
|
|
||||
help: Replace with `f"{b''}"`
|
||||
199 | def f(a: "'b' or 'c'"): ...
|
||||
200 |
|
||||
201 | # https://github.com/astral-sh/ruff/issues/20703
|
||||
- print(f"{b''}" or "bar") # SIM222
|
||||
202 + print(f"{b''}") # SIM222
|
||||
203 | x = 1
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
205 | (lambda: 1) or True # SIM222
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM222 [*] Use `f"{x=}"` instead of `f"{x=}" or ...`
|
||||
--> SIM222.py:204:7
|
||||
|
|
||||
202 | print(f"{b''}" or "bar") # SIM222
|
||||
203 | x = 1
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
205 | (lambda: 1) or True # SIM222
|
||||
206 | (i for i in range(1)) or "bar" # SIM222
|
||||
|
|
||||
help: Replace with `f"{x=}"`
|
||||
201 | # https://github.com/astral-sh/ruff/issues/20703
|
||||
202 | print(f"{b''}" or "bar") # SIM222
|
||||
203 | x = 1
|
||||
- print(f"{x=}" or "bar") # SIM222
|
||||
204 + print(f"{x=}") # SIM222
|
||||
205 | (lambda: 1) or True # SIM222
|
||||
206 | (i for i in range(1)) or "bar" # SIM222
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM222 [*] Use `lambda: 1` instead of `lambda: 1 or ...`
|
||||
--> SIM222.py:205:1
|
||||
|
|
||||
203 | x = 1
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
205 | (lambda: 1) or True # SIM222
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
206 | (i for i in range(1)) or "bar" # SIM222
|
||||
|
|
||||
help: Replace with `lambda: 1`
|
||||
202 | print(f"{b''}" or "bar") # SIM222
|
||||
203 | x = 1
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
- (lambda: 1) or True # SIM222
|
||||
205 + lambda: 1 # SIM222
|
||||
206 | (i for i in range(1)) or "bar" # SIM222
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
SIM222 [*] Use `(i for i in range(1))` instead of `(i for i in range(1)) or ...`
|
||||
--> SIM222.py:206:1
|
||||
|
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
205 | (lambda: 1) or True # SIM222
|
||||
206 | (i for i in range(1)) or "bar" # SIM222
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Replace with `(i for i in range(1))`
|
||||
203 | x = 1
|
||||
204 | print(f"{x=}" or "bar") # SIM222
|
||||
205 | (lambda: 1) or True # SIM222
|
||||
- (i for i in range(1)) or "bar" # SIM222
|
||||
206 + (i for i in range(1)) # SIM222
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
|
||||
use crate::Violation;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::{Violation, checkers::ast::Checker};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `break` statements outside of loops.
|
||||
@@ -26,3 +29,28 @@ impl Violation for BreakOutsideLoop {
|
||||
"`break` outside loop".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// F701
|
||||
pub(crate) fn break_outside_loop<'a>(
|
||||
checker: &Checker,
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) {
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match parent {
|
||||
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => {
|
||||
if !orelse.contains(child) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
checker.report_diagnostic(BreakOutsideLoop, stmt.range());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::{self as ast, Stmt};
|
||||
|
||||
use crate::Violation;
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::{Violation, checkers::ast::Checker};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `continue` statements outside of loops.
|
||||
@@ -26,3 +29,28 @@ impl Violation for ContinueOutsideLoop {
|
||||
"`continue` not properly in loop".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// F702
|
||||
pub(crate) fn continue_outside_loop<'a>(
|
||||
checker: &Checker,
|
||||
stmt: &'a Stmt,
|
||||
parents: &mut impl Iterator<Item = &'a Stmt>,
|
||||
) {
|
||||
let mut child = stmt;
|
||||
for parent in parents {
|
||||
match parent {
|
||||
Stmt::For(ast::StmtFor { orelse, .. }) | Stmt::While(ast::StmtWhile { orelse, .. }) => {
|
||||
if !orelse.contains(child) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
child = parent;
|
||||
}
|
||||
|
||||
checker.report_diagnostic(ContinueOutsideLoop, stmt.range());
|
||||
}
|
||||
|
||||
@@ -23,17 +23,16 @@ invalid-syntax: missing closing quote in string literal
|
||||
9 | # Unterminated f-string
|
||||
|
|
||||
|
||||
PLE2510 Invalid unescaped character backspace, use "\b" instead
|
||||
--> invalid_characters_syntax_error.py:7:6
|
||||
invalid-syntax: Expected a statement
|
||||
--> invalid_characters_syntax_error.py:7:7
|
||||
|
|
||||
5 | b = '␈'
|
||||
6 | # Unterminated string
|
||||
7 | b = '␈
|
||||
| ^
|
||||
| ^
|
||||
8 | b = '␈'
|
||||
9 | # Unterminated f-string
|
||||
|
|
||||
help: Replace with escape sequence
|
||||
|
||||
PLE2510 Invalid unescaped character backspace, use "\b" instead
|
||||
--> invalid_characters_syntax_error.py:8:6
|
||||
@@ -47,18 +46,6 @@ PLE2510 Invalid unescaped character backspace, use "\b" instead
|
||||
|
|
||||
help: Replace with escape sequence
|
||||
|
||||
PLE2510 Invalid unescaped character backspace, use "\b" instead
|
||||
--> invalid_characters_syntax_error.py:10:7
|
||||
|
|
||||
8 | b = '␈'
|
||||
9 | # Unterminated f-string
|
||||
10 | b = f'␈
|
||||
| ^
|
||||
11 | b = f'␈'
|
||||
12 | # Implicitly concatenated
|
||||
|
|
||||
help: Replace with escape sequence
|
||||
|
||||
invalid-syntax: f-string: unterminated string
|
||||
--> invalid_characters_syntax_error.py:10:7
|
||||
|
|
||||
@@ -122,12 +109,11 @@ invalid-syntax: missing closing quote in string literal
|
||||
| ^^
|
||||
|
|
||||
|
||||
PLE2510 Invalid unescaped character backspace, use "\b" instead
|
||||
--> invalid_characters_syntax_error.py:13:15
|
||||
invalid-syntax: Expected a statement
|
||||
--> invalid_characters_syntax_error.py:13:16
|
||||
|
|
||||
11 | b = f'␈'
|
||||
12 | # Implicitly concatenated
|
||||
13 | b = '␈' f'␈' '␈
|
||||
| ^
|
||||
| ^
|
||||
|
|
||||
help: Replace with escape sequence
|
||||
|
||||
@@ -19,7 +19,7 @@ mod tests {
|
||||
use crate::rules::{isort, pyupgrade};
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_diagnostics, assert_diagnostics_diff, settings};
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
#[test_case(Rule::ConvertNamedTupleFunctionalToClass, Path::new("UP014.py"))]
|
||||
#[test_case(Rule::ConvertTypedDictFunctionalToClass, Path::new("UP013.py"))]
|
||||
@@ -126,7 +126,6 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test_case(Rule::SuperCallWithParameters, Path::new("UP008.py"))]
|
||||
#[test_case(Rule::TypingTextStrAlias, Path::new("UP019.py"))]
|
||||
fn rules_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}__preview", path.to_string_lossy());
|
||||
let diagnostics = test_path(
|
||||
@@ -140,28 +139,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::NonPEP695TypeAlias, Path::new("UP040.py"))]
|
||||
#[test_case(Rule::NonPEP695TypeAlias, Path::new("UP040.pyi"))]
|
||||
#[test_case(Rule::NonPEP695GenericClass, Path::new("UP046_0.py"))]
|
||||
#[test_case(Rule::NonPEP695GenericClass, Path::new("UP046_1.py"))]
|
||||
#[test_case(Rule::NonPEP695GenericFunction, Path::new("UP047.py"))]
|
||||
fn type_var_default_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}__preview_diff", path.to_string_lossy());
|
||||
assert_diagnostics_diff!(
|
||||
snapshot,
|
||||
Path::new("pyupgrade").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Disabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_0.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_1.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_2.pyi"))]
|
||||
|
||||
@@ -6,7 +6,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::helpers::any_over_expr;
|
||||
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
||||
use ruff_python_ast::{self as ast, Expr, Keyword, StringFlags};
|
||||
use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||
use ruff_python_literal::format::{
|
||||
FieldName, FieldNamePart, FieldType, FormatPart, FormatString, FromTemplate,
|
||||
};
|
||||
@@ -430,7 +430,7 @@ pub(crate) fn f_strings(checker: &Checker, call: &ast::ExprCall, summary: &Forma
|
||||
// dot is the start of an attribute access.
|
||||
break token.start();
|
||||
}
|
||||
TokenKind::String if !token.unwrap_string_flags().is_unclosed() => {
|
||||
TokenKind::String => {
|
||||
match FStringConversion::try_convert(token.range(), &mut summary, checker.locator())
|
||||
{
|
||||
// If the format string contains side effects that would need to be repeated,
|
||||
|
||||
@@ -14,14 +14,13 @@ use ruff_python_ast::{
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_type_var_default_enabled;
|
||||
|
||||
pub(crate) use non_pep695_generic_class::*;
|
||||
pub(crate) use non_pep695_generic_function::*;
|
||||
pub(crate) use non_pep695_type_alias::*;
|
||||
pub(crate) use private_type_parameter::*;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
mod non_pep695_generic_class;
|
||||
mod non_pep695_generic_function;
|
||||
mod non_pep695_type_alias;
|
||||
@@ -123,10 +122,6 @@ impl Display for DisplayTypeVar<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(default) = self.type_var.default {
|
||||
f.write_str(" = ")?;
|
||||
f.write_str(&self.source[default.range()])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -138,63 +133,66 @@ impl<'a> From<&'a TypeVar<'a>> for TypeParam {
|
||||
name,
|
||||
restriction,
|
||||
kind,
|
||||
default,
|
||||
default: _, // TODO(brent) see below
|
||||
}: &'a TypeVar<'a>,
|
||||
) -> Self {
|
||||
let default = default.map(|expr| Box::new(expr.clone()));
|
||||
match kind {
|
||||
TypeParamKind::TypeVar => TypeParam::TypeVar(TypeParamTypeVar {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
bound: match restriction {
|
||||
Some(TypeVarRestriction::Bound(bound)) => Some(Box::new((*bound).clone())),
|
||||
Some(TypeVarRestriction::Constraint(constraints)) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: constraints.iter().map(|expr| (*expr).clone()).collect(),
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
Some(TypeVarRestriction::AnyStr) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: vec![
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("str"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("bytes"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
],
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
default,
|
||||
}),
|
||||
TypeParamKind::TypeVar => {
|
||||
TypeParam::TypeVar(TypeParamTypeVar {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
bound: match restriction {
|
||||
Some(TypeVarRestriction::Bound(bound)) => Some(Box::new((*bound).clone())),
|
||||
Some(TypeVarRestriction::Constraint(constraints)) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: constraints.iter().map(|expr| (*expr).clone()).collect(),
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
Some(TypeVarRestriction::AnyStr) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: vec![
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("str"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("bytes"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
],
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
// We don't handle defaults here yet. Should perhaps be a different rule since
|
||||
// defaults are only valid in 3.13+.
|
||||
default: None,
|
||||
})
|
||||
}
|
||||
TypeParamKind::TypeVarTuple => TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
default,
|
||||
default: None,
|
||||
}),
|
||||
TypeParamKind::ParamSpec => TypeParam::ParamSpec(TypeParamParamSpec {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
default,
|
||||
default: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -320,8 +318,8 @@ pub(crate) fn expr_name_to_type_var<'a>(
|
||||
.first()
|
||||
.is_some_and(Expr::is_string_literal_expr)
|
||||
{
|
||||
// `default` was added in PEP 696 and Python 3.13. We now support converting
|
||||
// TypeVars with defaults to PEP 695 type parameters.
|
||||
// TODO(brent) `default` was added in PEP 696 and Python 3.13 but can't be used in
|
||||
// generic type parameters before that
|
||||
//
|
||||
// ```python
|
||||
// T = TypeVar("T", default=Any, bound=str)
|
||||
@@ -369,22 +367,21 @@ fn in_nested_context(checker: &Checker) -> bool {
|
||||
}
|
||||
|
||||
/// Deduplicate `vars`, returning `None` if `vars` is empty or any duplicates are found.
|
||||
/// Also returns `None` if any `TypeVar` has a default value and preview mode is not enabled.
|
||||
fn check_type_vars<'a>(vars: Vec<TypeVar<'a>>, checker: &Checker) -> Option<Vec<TypeVar<'a>>> {
|
||||
fn check_type_vars(vars: Vec<TypeVar<'_>>) -> Option<Vec<TypeVar<'_>>> {
|
||||
if vars.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If any type variables have defaults and preview mode is not enabled, skip the rule
|
||||
if vars.iter().any(|tv| tv.default.is_some())
|
||||
&& !is_type_var_default_enabled(checker.settings())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// If any type variables were not unique, just bail out here. this is a runtime error and we
|
||||
// can't predict what the user wanted.
|
||||
(vars.iter().unique_by(|tvar| tvar.name).count() == vars.len()).then_some(vars)
|
||||
// can't predict what the user wanted. also bail out if any Python 3.13+ default values are
|
||||
// found on the type parameters
|
||||
(vars
|
||||
.iter()
|
||||
.unique_by(|tvar| tvar.name)
|
||||
.filter(|tvar| tvar.default.is_none())
|
||||
.count()
|
||||
== vars.len())
|
||||
.then_some(vars)
|
||||
}
|
||||
|
||||
/// Search `class_bases` for a `typing.Generic` base class. Returns the `Generic` expression (if
|
||||
|
||||
@@ -186,7 +186,7 @@ pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassD
|
||||
//
|
||||
// just because we can't confirm that `SomethingElse` is a `TypeVar`
|
||||
if !visitor.any_skipped {
|
||||
let Some(type_vars) = check_type_vars(visitor.vars, checker) else {
|
||||
let Some(type_vars) = check_type_vars(visitor.vars) else {
|
||||
diagnostic.defuse();
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -154,7 +154,7 @@ pub(crate) fn non_pep695_generic_function(checker: &Checker, function_def: &Stmt
|
||||
}
|
||||
}
|
||||
|
||||
let Some(type_vars) = check_type_vars(type_vars, checker) else {
|
||||
let Some(type_vars) = check_type_vars(type_vars) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssi
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_type_var_default_enabled;
|
||||
use crate::{Applicability, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
@@ -233,10 +232,8 @@ pub(crate) fn non_pep695_type_alias(checker: &Checker, stmt: &StmtAnnAssign) {
|
||||
.unique_by(|tvar| tvar.name)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Skip if any TypeVar has defaults and preview mode is not enabled
|
||||
if vars.iter().any(|tv| tv.default.is_some())
|
||||
&& !is_type_var_default_enabled(checker.settings())
|
||||
{
|
||||
// TODO(brent) handle `default` arg for Python 3.13+
|
||||
if vars.iter().any(|tv| tv.default.is_some()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
use ruff_python_ast::Expr;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_semantic::Modules;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_typing_extensions_str_alias_enabled;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of `typing.Text`.
|
||||
///
|
||||
/// In preview mode, also checks for `typing_extensions.Text`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `typing.Text` is an alias for `str`, and only exists for Python 2
|
||||
/// compatibility. As of Python 3.11, `typing.Text` is deprecated. Use `str`
|
||||
@@ -34,16 +30,14 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
/// ## References
|
||||
/// - [Python documentation: `typing.Text`](https://docs.python.org/3/library/typing.html#typing.Text)
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct TypingTextStrAlias {
|
||||
module: TypingModule,
|
||||
}
|
||||
pub(crate) struct TypingTextStrAlias;
|
||||
|
||||
impl Violation for TypingTextStrAlias {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("`{}.Text` is deprecated, use `str`", self.module)
|
||||
"`typing.Text` is deprecated, use `str`".to_string()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
@@ -53,26 +47,16 @@ impl Violation for TypingTextStrAlias {
|
||||
|
||||
/// UP019
|
||||
pub(crate) fn typing_text_str_alias(checker: &Checker, expr: &Expr) {
|
||||
if !checker
|
||||
.semantic()
|
||||
.seen_module(Modules::TYPING | Modules::TYPING_EXTENSIONS)
|
||||
{
|
||||
if !checker.semantic().seen_module(Modules::TYPING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(qualified_name) = checker.semantic().resolve_qualified_name(expr) {
|
||||
let segments = qualified_name.segments();
|
||||
let module = match segments {
|
||||
["typing", "Text"] => TypingModule::Typing,
|
||||
["typing_extensions", "Text"]
|
||||
if is_typing_extensions_str_alias_enabled(checker.settings()) =>
|
||||
{
|
||||
TypingModule::TypingExtensions
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let mut diagnostic = checker.report_diagnostic(TypingTextStrAlias { module }, expr.range());
|
||||
if checker
|
||||
.semantic()
|
||||
.resolve_qualified_name(expr)
|
||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["typing", "Text"]))
|
||||
{
|
||||
let mut diagnostic = checker.report_diagnostic(TypingTextStrAlias, expr.range());
|
||||
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_builtin_symbol(
|
||||
@@ -87,18 +71,3 @@ pub(crate) fn typing_text_str_alias(checker: &Checker, expr: &Expr) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum TypingModule {
|
||||
Typing,
|
||||
TypingExtensions,
|
||||
}
|
||||
|
||||
impl Display for TypingModule {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TypingModule::Typing => f.write_str("typing"),
|
||||
TypingModule::TypingExtensions => f.write_str("typing_extensions"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,5 +66,3 @@ help: Replace with `str`
|
||||
- def print_fourth_word(word: Goodbye) -> None:
|
||||
19 + def print_fourth_word(word: str) -> None:
|
||||
20 | print(word)
|
||||
21 |
|
||||
22 |
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||
--> UP019.py:7:22
|
||||
|
|
||||
7 | def print_word(word: Text) -> None:
|
||||
| ^^^^
|
||||
8 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
4 | from typing import Text as Goodbye
|
||||
5 |
|
||||
6 |
|
||||
- def print_word(word: Text) -> None:
|
||||
7 + def print_word(word: str) -> None:
|
||||
8 | print(word)
|
||||
9 |
|
||||
10 |
|
||||
|
||||
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||
--> UP019.py:11:29
|
||||
|
|
||||
11 | def print_second_word(word: typing.Text) -> None:
|
||||
| ^^^^^^^^^^^
|
||||
12 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
8 | print(word)
|
||||
9 |
|
||||
10 |
|
||||
- def print_second_word(word: typing.Text) -> None:
|
||||
11 + def print_second_word(word: str) -> None:
|
||||
12 | print(word)
|
||||
13 |
|
||||
14 |
|
||||
|
||||
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||
--> UP019.py:15:28
|
||||
|
|
||||
15 | def print_third_word(word: Hello.Text) -> None:
|
||||
| ^^^^^^^^^^
|
||||
16 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
12 | print(word)
|
||||
13 |
|
||||
14 |
|
||||
- def print_third_word(word: Hello.Text) -> None:
|
||||
15 + def print_third_word(word: str) -> None:
|
||||
16 | print(word)
|
||||
17 |
|
||||
18 |
|
||||
|
||||
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||
--> UP019.py:19:29
|
||||
|
|
||||
19 | def print_fourth_word(word: Goodbye) -> None:
|
||||
| ^^^^^^^
|
||||
20 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
16 | print(word)
|
||||
17 |
|
||||
18 |
|
||||
- def print_fourth_word(word: Goodbye) -> None:
|
||||
19 + def print_fourth_word(word: str) -> None:
|
||||
20 | print(word)
|
||||
21 |
|
||||
22 |
|
||||
|
||||
UP019 [*] `typing_extensions.Text` is deprecated, use `str`
|
||||
--> UP019.py:28:28
|
||||
|
|
||||
28 | def print_fifth_word(word: typing_extensions.Text) -> None:
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
29 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
25 | from typing_extensions import Text as TextAlias
|
||||
26 |
|
||||
27 |
|
||||
- def print_fifth_word(word: typing_extensions.Text) -> None:
|
||||
28 + def print_fifth_word(word: str) -> None:
|
||||
29 | print(word)
|
||||
30 |
|
||||
31 |
|
||||
|
||||
UP019 [*] `typing_extensions.Text` is deprecated, use `str`
|
||||
--> UP019.py:32:28
|
||||
|
|
||||
32 | def print_sixth_word(word: TypingExt.Text) -> None:
|
||||
| ^^^^^^^^^^^^^^
|
||||
33 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
29 | print(word)
|
||||
30 |
|
||||
31 |
|
||||
- def print_sixth_word(word: TypingExt.Text) -> None:
|
||||
32 + def print_sixth_word(word: str) -> None:
|
||||
33 | print(word)
|
||||
34 |
|
||||
35 |
|
||||
|
||||
UP019 [*] `typing_extensions.Text` is deprecated, use `str`
|
||||
--> UP019.py:36:30
|
||||
|
|
||||
36 | def print_seventh_word(word: TextAlias) -> None:
|
||||
| ^^^^^^^^^
|
||||
37 | print(word)
|
||||
|
|
||||
help: Replace with `str`
|
||||
33 | print(word)
|
||||
34 |
|
||||
35 |
|
||||
- def print_seventh_word(word: TextAlias) -> None:
|
||||
36 + def print_seventh_word(word: str) -> None:
|
||||
37 | print(word)
|
||||
@@ -217,7 +217,7 @@ UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of the `type` keywo
|
||||
44 | x: typing.TypeAlias = list[T]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
45 |
|
||||
46 | # `default` was added in Python 3.13
|
||||
46 | # `default` should be skipped for now, added in Python 3.13
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
41 |
|
||||
@@ -226,7 +226,7 @@ help: Use the `type` keyword
|
||||
- x: typing.TypeAlias = list[T]
|
||||
44 + type x = list[T]
|
||||
45 |
|
||||
46 | # `default` was added in Python 3.13
|
||||
46 | # `default` should be skipped for now, added in Python 3.13
|
||||
47 | T = typing.TypeVar("T", default=Any)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
@@ -355,26 +355,6 @@ help: Use the `type` keyword
|
||||
87 | # OK: Other name
|
||||
88 | T = TypeVar("T", bound=SupportGt)
|
||||
|
||||
UP040 [*] Type alias `AnyList` uses `TypeAliasType` assignment instead of the `type` keyword
|
||||
--> UP040.py:95:1
|
||||
|
|
||||
93 | # `default` was added in Python 3.13
|
||||
94 | T = typing.TypeVar("T", default=Any)
|
||||
95 | AnyList = TypeAliasType("AnyList", list[T], type_params=(T,))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
96 |
|
||||
97 | # unsafe fix if comments within the fix
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
92 |
|
||||
93 | # `default` was added in Python 3.13
|
||||
94 | T = typing.TypeVar("T", default=Any)
|
||||
- AnyList = TypeAliasType("AnyList", list[T], type_params=(T,))
|
||||
95 + type AnyList[T = Any] = list[T]
|
||||
96 |
|
||||
97 | # unsafe fix if comments within the fix
|
||||
98 | T = TypeVar("T")
|
||||
|
||||
UP040 [*] Type alias `PositiveList` uses `TypeAliasType` assignment instead of the `type` keyword
|
||||
--> UP040.py:99:1
|
||||
|
|
||||
@@ -489,8 +469,6 @@ UP040 [*] Type alias `T` uses `TypeAlias` annotation instead of the `type` keywo
|
||||
129 | | # comment7
|
||||
130 | | ) # comment8
|
||||
| |_^
|
||||
131 |
|
||||
132 | # Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
119 | | str
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 2
|
||||
|
||||
--- Added ---
|
||||
UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of the `type` keyword
|
||||
--> UP040.py:48:1
|
||||
|
|
||||
46 | # `default` was added in Python 3.13
|
||||
47 | T = typing.TypeVar("T", default=Any)
|
||||
48 | x: typing.TypeAlias = list[T]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
49 |
|
||||
50 | # OK
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
45 |
|
||||
46 | # `default` was added in Python 3.13
|
||||
47 | T = typing.TypeVar("T", default=Any)
|
||||
- x: typing.TypeAlias = list[T]
|
||||
48 + type x[T = Any] = list[T]
|
||||
49 |
|
||||
50 | # OK
|
||||
51 | x: TypeAlias
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
|
||||
UP040 [*] Type alias `DefaultList` uses `TypeAlias` annotation instead of the `type` keyword
|
||||
--> UP040.py:134:1
|
||||
|
|
||||
132 | # Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
133 | T_default = TypeVar("T_default", default=int)
|
||||
134 | DefaultList: TypeAlias = list[T_default]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
131 |
|
||||
132 | # Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
133 | T_default = TypeVar("T_default", default=int)
|
||||
- DefaultList: TypeAlias = list[T_default]
|
||||
134 + type DefaultList[T_default = int] = list[T_default]
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 0
|
||||
@@ -1,48 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 2
|
||||
|
||||
--- Added ---
|
||||
UP046 [*] Generic class `DefaultTypeVar` uses `Generic` subclass instead of type parameters
|
||||
--> UP046_0.py:129:22
|
||||
|
|
||||
129 | class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
|
||||
| ^^^^^^^^^^
|
||||
130 | var: V
|
||||
|
|
||||
help: Use type parameters
|
||||
126 | V = TypeVar("V", default=Any, bound=str)
|
||||
127 |
|
||||
128 |
|
||||
- class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
|
||||
129 + class DefaultTypeVar[V: str = Any]: # -> [V: str = Any]
|
||||
130 | var: V
|
||||
131 |
|
||||
132 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
|
||||
UP046 [*] Generic class `DefaultOnlyTypeVar` uses `Generic` subclass instead of type parameters
|
||||
--> UP046_0.py:137:26
|
||||
|
|
||||
137 | class DefaultOnlyTypeVar(Generic[W]): # -> [W = int]
|
||||
| ^^^^^^^^^^
|
||||
138 | var: W
|
||||
|
|
||||
help: Use type parameters
|
||||
134 | W = TypeVar("W", default=int)
|
||||
135 |
|
||||
136 |
|
||||
- class DefaultOnlyTypeVar(Generic[W]): # -> [W = int]
|
||||
137 + class DefaultOnlyTypeVar[W = int]: # -> [W = int]
|
||||
138 | var: W
|
||||
139 |
|
||||
140 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 0
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 1
|
||||
|
||||
--- Added ---
|
||||
UP047 [*] Generic function `default_var` should use type parameters
|
||||
--> UP047.py:51:5
|
||||
|
|
||||
51 | def default_var(v: V) -> V:
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
52 | return v
|
||||
|
|
||||
help: Use type parameters
|
||||
48 | V = TypeVar("V", default=Any, bound=str)
|
||||
49 |
|
||||
50 |
|
||||
- def default_var(v: V) -> V:
|
||||
51 + def default_var[V: str = Any](v: V) -> V:
|
||||
52 | return v
|
||||
53 |
|
||||
54 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
@@ -48,7 +48,7 @@ use crate::{AlwaysFixableViolation, Edit, Fix};
|
||||
/// element. As such, any side effects that occur during iteration will be
|
||||
/// delayed.
|
||||
/// 2. Second, accessing members of a collection via square bracket notation
|
||||
/// `[0]` or the `pop()` function will raise `IndexError` if the collection
|
||||
/// `[0]` of the `pop()` function will raise `IndexError` if the collection
|
||||
/// is empty, while `next(iter(...))` will raise `StopIteration`.
|
||||
///
|
||||
/// ## References
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::parenthesize::parenthesized_range;
|
||||
use crate::statement_visitor::StatementVisitor;
|
||||
use crate::visitor::Visitor;
|
||||
use crate::{
|
||||
self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral,
|
||||
self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr,
|
||||
InterpolatedStringElement, MatchCase, Operator, Pattern, Stmt, TypeParam,
|
||||
};
|
||||
use crate::{AnyNodeRef, ExprContext};
|
||||
@@ -1219,8 +1219,6 @@ impl Truthiness {
|
||||
F: Fn(&str) -> bool,
|
||||
{
|
||||
match expr {
|
||||
Expr::Lambda(_) => Self::Truthy,
|
||||
Expr::Generator(_) => Self::Truthy,
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||
if value.is_empty() {
|
||||
Self::Falsey
|
||||
@@ -1390,9 +1388,7 @@ fn is_non_empty_f_string(expr: &ast::ExprFString) -> bool {
|
||||
Expr::FString(f_string) => is_non_empty_f_string(f_string),
|
||||
// These literals may or may not be empty.
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => !value.is_empty(),
|
||||
// Confusingly, f"{b""}" renders as the string 'b""', which is non-empty.
|
||||
// Therefore, any bytes interpolation is guaranteed non-empty when stringified.
|
||||
Expr::BytesLiteral(_) => true,
|
||||
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => !value.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1401,9 +1397,7 @@ fn is_non_empty_f_string(expr: &ast::ExprFString) -> bool {
|
||||
ast::FStringPart::FString(f_string) => {
|
||||
f_string.elements.iter().all(|element| match element {
|
||||
InterpolatedStringElement::Literal(string_literal) => !string_literal.is_empty(),
|
||||
InterpolatedStringElement::Interpolation(f_string) => {
|
||||
f_string.debug_text.is_some() || inner(&f_string.expression)
|
||||
}
|
||||
InterpolatedStringElement::Interpolation(f_string) => inner(&f_string.expression),
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1499,7 +1493,7 @@ pub fn pep_604_optional(expr: &Expr) -> Expr {
|
||||
ast::ExprBinOp {
|
||||
left: Box::new(expr.clone()),
|
||||
op: Operator::BitOr,
|
||||
right: Box::new(Expr::NoneLiteral(ExprNoneLiteral::default())),
|
||||
right: Box::new(Expr::NoneLiteral(ast::ExprNoneLiteral::default())),
|
||||
range: TextRange::default(),
|
||||
node_index: AtomicNodeIndex::NONE,
|
||||
}
|
||||
|
||||
@@ -735,8 +735,6 @@ pub trait StringFlags: Copy {
|
||||
|
||||
fn prefix(self) -> AnyStringPrefix;
|
||||
|
||||
fn is_unclosed(self) -> bool;
|
||||
|
||||
/// Is the string triple-quoted, i.e.,
|
||||
/// does it begin and end with three consecutive quote characters?
|
||||
fn is_triple_quoted(self) -> bool {
|
||||
@@ -781,7 +779,6 @@ pub trait StringFlags: Copy {
|
||||
|
||||
fn as_any_string_flags(self) -> AnyStringFlags {
|
||||
AnyStringFlags::new(self.prefix(), self.quote_style(), self.triple_quotes())
|
||||
.with_unclosed(self.is_unclosed())
|
||||
}
|
||||
|
||||
fn display_contents(self, contents: &str) -> DisplayFlags<'_> {
|
||||
@@ -832,10 +829,6 @@ bitflags! {
|
||||
/// for why we track the casing of the `r` prefix,
|
||||
/// but not for any other prefix
|
||||
const R_PREFIX_UPPER = 1 << 3;
|
||||
|
||||
/// The f-string is unclosed, meaning it is missing a closing quote.
|
||||
/// For example: `f"{bar`
|
||||
const UNCLOSED = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,12 +887,6 @@ impl FStringFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(InterpolatedStringFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(mut self, prefix: FStringPrefix) -> Self {
|
||||
match prefix {
|
||||
@@ -997,12 +984,6 @@ impl TStringFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(InterpolatedStringFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(mut self, prefix: TStringPrefix) -> Self {
|
||||
match prefix {
|
||||
@@ -1070,10 +1051,6 @@ impl StringFlags for FStringFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Format(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(InterpolatedStringFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FStringFlags {
|
||||
@@ -1082,7 +1059,6 @@ impl fmt::Debug for FStringFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1114,10 +1090,6 @@ impl StringFlags for TStringFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Template(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(InterpolatedStringFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TStringFlags {
|
||||
@@ -1126,7 +1098,6 @@ impl fmt::Debug for TStringFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1456,9 +1427,6 @@ bitflags! {
|
||||
|
||||
/// The string was deemed invalid by the parser.
|
||||
const INVALID = 1 << 5;
|
||||
|
||||
/// The string literal misses the matching closing quote(s).
|
||||
const UNCLOSED = 1 << 6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1511,12 +1479,6 @@ impl StringLiteralFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(StringLiteralFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(self, prefix: StringLiteralPrefix) -> Self {
|
||||
let StringLiteralFlags(flags) = self;
|
||||
@@ -1598,10 +1560,6 @@ impl StringFlags for StringLiteralFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Regular(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(StringLiteralFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StringLiteralFlags {
|
||||
@@ -1610,7 +1568,6 @@ impl fmt::Debug for StringLiteralFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1889,9 +1846,6 @@ bitflags! {
|
||||
|
||||
/// The bytestring was deemed invalid by the parser.
|
||||
const INVALID = 1 << 4;
|
||||
|
||||
/// The byte string misses the matching closing quote(s).
|
||||
const UNCLOSED = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1943,12 +1897,6 @@ impl BytesLiteralFlags {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(BytesLiteralFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_prefix(mut self, prefix: ByteStringPrefix) -> Self {
|
||||
match prefix {
|
||||
@@ -2011,10 +1959,6 @@ impl StringFlags for BytesLiteralFlags {
|
||||
fn prefix(self) -> AnyStringPrefix {
|
||||
AnyStringPrefix::Bytes(self.prefix())
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(BytesLiteralFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BytesLiteralFlags {
|
||||
@@ -2023,7 +1967,6 @@ impl fmt::Debug for BytesLiteralFlags {
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -2085,7 +2028,7 @@ bitflags! {
|
||||
/// prefix flags is by calling the `as_flags()` method on the
|
||||
/// `StringPrefix` enum.
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct AnyStringFlagsInner: u16 {
|
||||
struct AnyStringFlagsInner: u8 {
|
||||
/// The string uses double quotes (`"`).
|
||||
/// If this flag is not set, the string uses single quotes (`'`).
|
||||
const DOUBLE = 1 << 0;
|
||||
@@ -2128,9 +2071,6 @@ bitflags! {
|
||||
/// for why we track the casing of the `r` prefix,
|
||||
/// but not for any other prefix
|
||||
const R_PREFIX_UPPER = 1 << 7;
|
||||
|
||||
/// String without matching closing quote(s).
|
||||
const UNCLOSED = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2226,12 +2166,6 @@ impl AnyStringFlags {
|
||||
.set(AnyStringFlagsInner::TRIPLE_QUOTED, triple_quotes.is_yes());
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_unclosed(mut self, unclosed: bool) -> Self {
|
||||
self.0.set(AnyStringFlagsInner::UNCLOSED, unclosed);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl StringFlags for AnyStringFlags {
|
||||
@@ -2300,10 +2234,6 @@ impl StringFlags for AnyStringFlags {
|
||||
}
|
||||
AnyStringPrefix::Regular(StringLiteralPrefix::Empty)
|
||||
}
|
||||
|
||||
fn is_unclosed(self) -> bool {
|
||||
self.0.intersects(AnyStringFlagsInner::UNCLOSED)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AnyStringFlags {
|
||||
@@ -2312,7 +2242,6 @@ impl fmt::Debug for AnyStringFlags {
|
||||
.field("prefix", &self.prefix())
|
||||
.field("triple_quoted", &self.is_triple_quoted())
|
||||
.field("quote_style", &self.quote_style())
|
||||
.field("unclosed", &self.is_unclosed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -2329,7 +2258,6 @@ impl From<AnyStringFlags> for StringLiteralFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2351,7 +2279,6 @@ impl From<AnyStringFlags> for BytesLiteralFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(bytestring_prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2373,7 +2300,6 @@ impl From<AnyStringFlags> for FStringFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2395,7 +2321,6 @@ impl From<AnyStringFlags> for TStringFlags {
|
||||
.with_quote_style(value.quote_style())
|
||||
.with_prefix(prefix)
|
||||
.with_triple_quotes(value.triple_quotes())
|
||||
.with_unclosed(value.is_unclosed())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# regression test for #1765
|
||||
class Foo:
|
||||
def foo(self):
|
||||
if True:
|
||||
content_ids: Mapping[
|
||||
str, Optional[ContentId]
|
||||
] = self.publisher_content_store.store_config_contents(files)
|
||||
@@ -1,7 +0,0 @@
|
||||
# regression test for #1765
|
||||
class Foo:
|
||||
def foo(self):
|
||||
if True:
|
||||
content_ids: Mapping[str, Optional[ContentId]] = (
|
||||
self.publisher_content_store.store_config_contents(files)
|
||||
)
|
||||
@@ -1 +1 @@
|
||||
{"target_version": "3.10"}
|
||||
{"target_version": "3.10"}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# long variable name
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 0
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = 1 # with a comment
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [
|
||||
1, 2, 3
|
||||
]
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function()
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||
arg1, arg2, arg3
|
||||
)
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||
)
|
||||
# long function name
|
||||
normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying()
|
||||
normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
|
||||
arg1, arg2, arg3
|
||||
)
|
||||
normal_name = but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
|
||||
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||
)
|
||||
string_variable_name = (
|
||||
"a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa
|
||||
)
|
||||
for key in """
|
||||
hostname
|
||||
port
|
||||
username
|
||||
""".split():
|
||||
if key in self.connect_kwargs:
|
||||
raise ValueError(err.format(key))
|
||||
concatenated_strings = "some strings that are " "concatenated implicitly, so if you put them on separate " "lines it will fit"
|
||||
del concatenated_strings, string_variable_name, normal_function_name, normal_name, need_more_to_make_the_line_long_enough
|
||||
del ([], name_1, name_2), [(), [], name_4, name_3], name_1[[name_2 for name_1 in name_0]]
|
||||
del (),
|
||||
@@ -1,61 +0,0 @@
|
||||
# long variable name
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = (
|
||||
0
|
||||
)
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = (
|
||||
1 # with a comment
|
||||
)
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = (
|
||||
function()
|
||||
)
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||
arg1, arg2, arg3
|
||||
)
|
||||
this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it = function(
|
||||
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||
)
|
||||
# long function name
|
||||
normal_name = (
|
||||
but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying()
|
||||
)
|
||||
normal_name = (
|
||||
but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
|
||||
arg1, arg2, arg3
|
||||
)
|
||||
)
|
||||
normal_name = (
|
||||
but_the_function_name_is_now_ridiculously_long_and_it_is_still_super_annoying(
|
||||
[1, 2, 3], arg1, [1, 2, 3], arg2, [1, 2, 3], arg3
|
||||
)
|
||||
)
|
||||
string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa
|
||||
for key in """
|
||||
hostname
|
||||
port
|
||||
username
|
||||
""".split():
|
||||
if key in self.connect_kwargs:
|
||||
raise ValueError(err.format(key))
|
||||
concatenated_strings = (
|
||||
"some strings that are "
|
||||
"concatenated implicitly, so if you put them on separate "
|
||||
"lines it will fit"
|
||||
)
|
||||
del (
|
||||
concatenated_strings,
|
||||
string_variable_name,
|
||||
normal_function_name,
|
||||
normal_name,
|
||||
need_more_to_make_the_line_long_enough,
|
||||
)
|
||||
del (
|
||||
([], name_1, name_2),
|
||||
[(), [], name_4, name_3],
|
||||
name_1[[name_2 for name_1 in name_0]],
|
||||
)
|
||||
del ((),)
|
||||
@@ -1 +1 @@
|
||||
{"target_version": "3.8"}
|
||||
{"target_version": "3.8"}
|
||||
|
||||
@@ -82,9 +82,3 @@ async def func():
|
||||
argument1, argument2, argument3="some_value"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
# don't remove the brackets here, it changes the meaning of the code.
|
||||
with (x, y) as z:
|
||||
pass
|
||||
|
||||
@@ -83,8 +83,3 @@ async def func():
|
||||
some_other_function(argument1, argument2, argument3="some_value"),
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
# don't remove the brackets here, it changes the meaning of the code.
|
||||
with (x, y) as z:
|
||||
pass
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
"""
|
||||
87 characters ............................................................................
|
||||
"""
|
||||
@@ -1,3 +0,0 @@
|
||||
"""
|
||||
87 characters ............................................................................
|
||||
"""
|
||||
@@ -1,8 +0,0 @@
|
||||
def foo(): return "mock" # fmt: skip
|
||||
if True: print("yay") # fmt: skip
|
||||
for i in range(10): print(i) # fmt: skip
|
||||
|
||||
j = 1 # fmt: skip
|
||||
while j < 10: j += 1 # fmt: skip
|
||||
|
||||
b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip
|
||||
@@ -1,8 +0,0 @@
|
||||
def foo(): return "mock" # fmt: skip
|
||||
if True: print("yay") # fmt: skip
|
||||
for i in range(10): print(i) # fmt: skip
|
||||
|
||||
j = 1 # fmt: skip
|
||||
while j < 10: j += 1 # fmt: skip
|
||||
|
||||
b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip
|
||||
@@ -1,6 +0,0 @@
|
||||
def foo():
|
||||
pass
|
||||
|
||||
|
||||
# comment 1 # fmt: skip
|
||||
# comment 2
|
||||
@@ -1,6 +0,0 @@
|
||||
def foo():
|
||||
pass
|
||||
|
||||
|
||||
# comment 1 # fmt: skip
|
||||
# comment 2
|
||||
@@ -1,15 +0,0 @@
|
||||
x = "\x1F"
|
||||
x = "\\x1B"
|
||||
x = "\\\x1B"
|
||||
x = "\U0001F60E"
|
||||
x = "\u0001F60E"
|
||||
x = r"\u0001F60E"
|
||||
x = "don't format me"
|
||||
x = "\xA3"
|
||||
x = "\u2717"
|
||||
x = "\uFaCe"
|
||||
x = "\N{ox}\N{OX}"
|
||||
x = "\N{lAtIn smaLL letteR x}"
|
||||
x = "\N{CYRILLIC small LETTER BYELORUSSIAN-UKRAINIAN I}"
|
||||
x = b"\x1Fdon't byte"
|
||||
x = rb"\x1Fdon't format"
|
||||
@@ -1,15 +0,0 @@
|
||||
x = "\x1f"
|
||||
x = "\\x1B"
|
||||
x = "\\\x1b"
|
||||
x = "\U0001f60e"
|
||||
x = "\u0001F60E"
|
||||
x = r"\u0001F60E"
|
||||
x = "don't format me"
|
||||
x = "\xa3"
|
||||
x = "\u2717"
|
||||
x = "\uface"
|
||||
x = "\N{OX}\N{OX}"
|
||||
x = "\N{LATIN SMALL LETTER X}"
|
||||
x = "\N{CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I}"
|
||||
x = b"\x1fdon't byte"
|
||||
x = rb"\x1Fdon't format"
|
||||
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.options.json
vendored
Normal file
1
crates/ruff_python_formatter/resources/test/fixtures/black/cases/fstring.options.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"target_version": "3.12"}
|
||||
@@ -1,34 +0,0 @@
|
||||
# Regression tests for long f-strings, including examples from issue #3623
|
||||
|
||||
a = (
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
)
|
||||
|
||||
a = (
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
)
|
||||
|
||||
a = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + \
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
|
||||
a = f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + \
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
|
||||
a = (
|
||||
f'bbbbbbb"{"b"}"'
|
||||
'aaaaaaaa'
|
||||
)
|
||||
|
||||
a = (
|
||||
f'"{"b"}"'
|
||||
)
|
||||
|
||||
a = (
|
||||
f'\"{"b"}\"'
|
||||
)
|
||||
|
||||
a = (
|
||||
r'\"{"b"}\"'
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
# Regression tests for long f-strings, including examples from issue #3623
|
||||
|
||||
a = (
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
)
|
||||
|
||||
a = (
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
)
|
||||
|
||||
a = (
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
+ f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
)
|
||||
|
||||
a = (
|
||||
f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
+ f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"'
|
||||
)
|
||||
|
||||
a = f'bbbbbbb"{"b"}"' "aaaaaaaa"
|
||||
|
||||
a = f'"{"b"}"'
|
||||
|
||||
a = f'"{"b"}"'
|
||||
|
||||
a = r'\"{"b"}\"'
|
||||
@@ -1 +1 @@
|
||||
{"target_version": "3.10"}
|
||||
{"preview": "enabled", "target_version": "3.10"}
|
||||
@@ -1 +0,0 @@
|
||||
{"target_version": "3.12"}
|
||||
@@ -1,133 +0,0 @@
|
||||
def plain[T, B](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
def arg_magic[T, B](a: T, b: T,) -> T:
|
||||
return a
|
||||
|
||||
def type_param_magic[T, B,](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
def both_magic[T, B,](a: T, b: T,) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def plain_multiline[
|
||||
T,
|
||||
B
|
||||
](
|
||||
a: T,
|
||||
b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def arg_magic_multiline[
|
||||
T,
|
||||
B
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def type_param_magic_multiline[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T,
|
||||
b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def both_magic_multiline[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def plain_mixed1[
|
||||
T,
|
||||
B
|
||||
](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
def plain_mixed2[T, B](
|
||||
a: T,
|
||||
b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def arg_magic_mixed1[
|
||||
T,
|
||||
B
|
||||
](a: T, b: T,) -> T:
|
||||
return a
|
||||
|
||||
def arg_magic_mixed2[T, B](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def type_param_magic_mixed1[
|
||||
T,
|
||||
B,
|
||||
](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
def type_param_magic_mixed2[T, B,](
|
||||
a: T,
|
||||
b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def both_magic_mixed1[
|
||||
T,
|
||||
B,
|
||||
](a: T, b: T,) -> T:
|
||||
return a
|
||||
|
||||
def both_magic_mixed2[T, B,](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
def something_something_function[
|
||||
T: Model
|
||||
](param: list[int], other_param: type[T], *, some_other_param: bool = True) -> QuerySet[
|
||||
T
|
||||
]:
|
||||
pass
|
||||
|
||||
|
||||
def func[A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere, LIKE_THIS, AND_THIS, ANOTHER_ONE, AND_YET_ANOTHER_ONE: ThisOneHasTyping](a: T, b: T, c: T, d: T, e: T, f: T, g: T, h: T, i: T, j: T, k: T, l: T, m: T, n: T, o: T, p: T) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def with_random_comments[
|
||||
Z
|
||||
# bye
|
||||
]():
|
||||
return a
|
||||
|
||||
|
||||
def func[
|
||||
T, # comment
|
||||
U # comment
|
||||
,
|
||||
Z: # comment
|
||||
int
|
||||
](): pass
|
||||
|
||||
|
||||
def func[
|
||||
T, # comment but it's long so it doesn't just move to the end of the line
|
||||
U # comment comment comm comm ent ent
|
||||
,
|
||||
Z: # comment ent ent comm comm comment
|
||||
int
|
||||
](): pass
|
||||
@@ -1,170 +0,0 @@
|
||||
def plain[T, B](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def arg_magic[T, B](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def type_param_magic[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T, b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def both_magic[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def plain_multiline[T, B](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def arg_magic_multiline[T, B](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def type_param_magic_multiline[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T, b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def both_magic_multiline[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def plain_mixed1[T, B](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def plain_mixed2[T, B](a: T, b: T) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def arg_magic_mixed1[T, B](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def arg_magic_mixed2[T, B](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def type_param_magic_mixed1[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T, b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def type_param_magic_mixed2[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T, b: T
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def both_magic_mixed1[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def both_magic_mixed2[
|
||||
T,
|
||||
B,
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def something_something_function[T: Model](
|
||||
param: list[int], other_param: type[T], *, some_other_param: bool = True
|
||||
) -> QuerySet[T]:
|
||||
pass
|
||||
|
||||
|
||||
def func[
|
||||
A_LOT_OF_GENERIC_TYPES: AreBeingDefinedHere,
|
||||
LIKE_THIS,
|
||||
AND_THIS,
|
||||
ANOTHER_ONE,
|
||||
AND_YET_ANOTHER_ONE: ThisOneHasTyping,
|
||||
](
|
||||
a: T,
|
||||
b: T,
|
||||
c: T,
|
||||
d: T,
|
||||
e: T,
|
||||
f: T,
|
||||
g: T,
|
||||
h: T,
|
||||
i: T,
|
||||
j: T,
|
||||
k: T,
|
||||
l: T,
|
||||
m: T,
|
||||
n: T,
|
||||
o: T,
|
||||
p: T,
|
||||
) -> T:
|
||||
return a
|
||||
|
||||
|
||||
def with_random_comments[
|
||||
Z
|
||||
# bye
|
||||
]():
|
||||
return a
|
||||
|
||||
|
||||
def func[T, U, Z: int](): # comment # comment # comment
|
||||
pass
|
||||
|
||||
|
||||
def func[
|
||||
T, # comment but it's long so it doesn't just move to the end of the line
|
||||
U, # comment comment comm comm ent ent
|
||||
Z: int, # comment ent ent comm comm comment
|
||||
]():
|
||||
pass
|
||||
@@ -6,7 +6,7 @@ def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parame
|
||||
def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
|
||||
def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
|
||||
|
||||
# Adding some unformated code covering a wide range of syntaxes.
|
||||
# Adding some unformatted code covering a wide range of syntaxes.
|
||||
|
||||
if True:
|
||||
# Incorrectly indented prefix comments.
|
||||
|
||||
@@ -28,7 +28,7 @@ def foo3(
|
||||
|
||||
def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass
|
||||
|
||||
# Adding some unformated code covering a wide range of syntaxes.
|
||||
# Adding some unformatted code covering a wide range of syntaxes.
|
||||
|
||||
if True:
|
||||
# Incorrectly indented prefix comments.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# flags: --line-ranges=6-7
|
||||
class Foo:
|
||||
|
||||
@overload
|
||||
def foo(): ...
|
||||
|
||||
def fox(self):
|
||||
print()
|
||||
@@ -1,8 +0,0 @@
|
||||
# flags: --line-ranges=6-7
|
||||
class Foo:
|
||||
|
||||
@overload
|
||||
def foo(): ...
|
||||
|
||||
def fox(self):
|
||||
print()
|
||||
@@ -1,28 +0,0 @@
|
||||
def func(
|
||||
arg1,
|
||||
arg2,
|
||||
) -> Set["this_is_a_very_long_module_name.AndAVeryLongClasName"
|
||||
".WithAVeryVeryVeryVeryVeryLongSubClassName"]:
|
||||
pass
|
||||
|
||||
|
||||
def func(
|
||||
argument: (
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||
),
|
||||
) -> (
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def func(
|
||||
argument: (
|
||||
"int |"
|
||||
"str"
|
||||
),
|
||||
) -> Set["int |"
|
||||
" str"]:
|
||||
pass
|
||||
@@ -1,26 +0,0 @@
|
||||
def func(
|
||||
arg1,
|
||||
arg2,
|
||||
) -> Set[
|
||||
"this_is_a_very_long_module_name.AndAVeryLongClasName"
|
||||
".WithAVeryVeryVeryVeryVeryLongSubClassName"
|
||||
]:
|
||||
pass
|
||||
|
||||
|
||||
def func(
|
||||
argument: (
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||
),
|
||||
) -> (
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[int] |"
|
||||
"VeryLongClassNameWithAwkwardGenericSubtype[str]"
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
def func(
|
||||
argument: "int |" "str",
|
||||
) -> Set["int |" " str"]:
|
||||
pass
|
||||
@@ -1,6 +1,6 @@
|
||||
"""I am a very helpful module docstring.
|
||||
|
||||
With trailing spaces:
|
||||
With trailing spaces (only removed with unify_docstring_detection on):
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Ut enim ad minim veniam,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""I am a very helpful module docstring.
|
||||
|
||||
With trailing spaces:
|
||||
With trailing spaces (only removed with unify_docstring_detection on):
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Ut enim ad minim veniam,
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/python
|
||||
|
||||
# regression test for #4762
|
||||
"""
|
||||
docstring
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/python
|
||||
|
||||
# regression test for #4762
|
||||
"""
|
||||
docstring
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@@ -25,4 +25,5 @@ class MultilineDocstringsAsWell:
|
||||
|
||||
|
||||
class SingleQuotedDocstring:
|
||||
|
||||
"I'm a docstring but I don't even get triple quotes."
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"target_version": "3.10"}
|
||||
{"preview": "enabled", "target_version": "3.10"}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user