diff --git a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap index f35c93029c984..da3eb66afc453 100644 --- a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap +++ b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above-2.snap @@ -9,7 +9,6 @@ info: - concise - "--show-settings" - test.py -snapshot_kind: text --- success: true exit_code: 0 @@ -265,7 +264,7 @@ linter.ruff.parenthesize_tuple_in_subscript = false # Formatter Settings formatter.exclude = [] -formatter.unresolved_target_version = 3.9 +formatter.unresolved_target_version = 3.10 formatter.per_file_target_version = {} formatter.preview = disabled formatter.line_width = 88 @@ -280,7 +279,7 @@ formatter.docstring_code_line_width = dynamic # Analyze Settings analyze.exclude = [] analyze.preview = disabled -analyze.target_version = 3.9 +analyze.target_version = 3.10 analyze.string_imports = disabled analyze.extension = ExtensionMapping({}) analyze.include_dependencies = {} diff --git a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap index 980eaf47adc2b..e9ca7bd4000a8 100644 --- a/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap +++ b/crates/ruff/tests/cli/snapshots/cli__lint__requires_python_ruff_toml_above.snap @@ -9,7 +9,6 @@ info: - concise - "--show-settings" - foo/test.py -snapshot_kind: text --- success: true exit_code: 0 @@ -265,7 +264,7 @@ linter.ruff.parenthesize_tuple_in_subscript = false # Formatter Settings formatter.exclude = [] -formatter.unresolved_target_version = 3.9 +formatter.unresolved_target_version = 3.10 formatter.per_file_target_version = {} formatter.preview = disabled formatter.line_width = 88 @@ -280,7 +279,7 @@ formatter.docstring_code_line_width = dynamic # Analyze Settings analyze.exclude = [] analyze.preview = disabled -analyze.target_version = 3.9 +analyze.target_version = 3.10 analyze.string_imports = disabled analyze.extension = ExtensionMapping({}) analyze.include_dependencies = {} diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py index 8576a1d403d38..eaae5e5ee3ace 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_18.py @@ -14,6 +14,6 @@ class Bar: ] -# OK: Allow named expressions in annotations. +# This is no longer allowed on Python 3.14+ x: (y := 1) print(y) diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py index f87819ef8004b..3eaa0d8a4e0ed 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py @@ -13,16 +13,16 @@ # References to a class from inside the class: class C: - other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file + other: C = ... # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations other2: "C" = ... # always okay - def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file + def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations def from_str2(self, s: str) -> "C": ... # always okay # Circular references: class A: - foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file + foo: B # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations foo2: "B" # always okay - bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file + bar: dict[str, B] # valid in a `.pyi` stub file, and in a `.py` runtime file with deferred annotations bar2: dict[str, "A"] # always okay class B: diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index 339a4f4aa5595..56e180bfabacd 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -27,14 +27,13 @@ use crate::fix::{FixResult, fix_file}; use crate::message::create_syntax_error_diagnostic; use crate::noqa::add_noqa; use crate::package::PackageRoot; -use crate::preview::is_py314_support_enabled; use crate::registry::Rule; #[cfg(any(feature = "test-rules", test))] use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule}; use crate::settings::types::UnsafeFixes; use crate::settings::{LinterSettings, TargetVersion, flags}; use crate::source_kind::SourceKind; -use crate::{Locator, directives, fs, warn_user_once}; +use crate::{Locator, directives, fs}; pub(crate) mod float; @@ -442,14 +441,6 @@ pub fn lint_only( ) -> LinterResult { let target_version = settings.resolve_target_version(path); - if matches!(target_version, TargetVersion(Some(PythonVersion::PY314))) - && !is_py314_support_enabled(settings) - { - warn_user_once!( - "Support for Python 3.14 is in preview and may undergo breaking changes. Enable `preview` to remove this warning." - ); - } - let parsed = source.into_parsed(source_kind, source_type, target_version.parser_version()); // Map row and column locations to byte slices (lazily). @@ -551,14 +542,6 @@ pub fn lint_fix<'a>( let target_version = settings.resolve_target_version(path); - if matches!(target_version, TargetVersion(Some(PythonVersion::PY314))) - && !is_py314_support_enabled(settings) - { - warn_user_once!( - "Support for Python 3.14 is in preview and may undergo breaking changes. Enable `preview` to remove this warning." - ); - } - // Continuously fix until the source code stabilizes. loop { // Parse once. diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index a5647d5ea0638..37a577e8df187 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -7,10 +7,6 @@ use crate::settings::LinterSettings; -pub(crate) const fn is_py314_support_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - // Rule-specific behavior // https://github.com/astral-sh/ruff/pull/15541 diff --git a/crates/ruff_linter/src/rules/fastapi/mod.rs b/crates/ruff_linter/src/rules/fastapi/mod.rs index f6e48dd4ada0b..17b3cb663c140 100644 --- a/crates/ruff_linter/src/rules/fastapi/mod.rs +++ b/crates/ruff_linter/src/rules/fastapi/mod.rs @@ -8,9 +8,12 @@ mod tests { use anyhow::Result; use test_case::test_case; + use ruff_python_ast::PythonVersion; + use crate::registry::Rule; + use crate::settings::LinterSettings; use crate::test::test_path; - use crate::{assert_diagnostics, settings}; + use crate::{assert_diagnostics, assert_diagnostics_diff}; #[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))] #[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))] @@ -20,12 +23,35 @@ mod tests { let snapshot = format!("{}_{}", rule_code.name(), path.to_string_lossy()); let diagnostics = test_path( Path::new("fastapi").join(path).as_path(), - &settings::LinterSettings::for_rule(rule_code), + &LinterSettings::for_rule(rule_code), )?; assert_diagnostics!(snapshot, diagnostics); Ok(()) } + #[test_case(Rule::FastApiRedundantResponseModel, Path::new("FAST001.py"))] + #[test_case(Rule::FastApiUnusedPathParameter, Path::new("FAST003.py"))] + fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "deferred_annotations_diff_{}_{}", + rule_code.name(), + path.to_string_lossy() + ); + assert_diagnostics_diff!( + snapshot, + Path::new("fastapi").join(path).as_path(), + &LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + ..LinterSettings::for_rule(rule_code) + }, + &LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + ..LinterSettings::for_rule(rule_code) + }, + ); + Ok(()) + } + // FAST002 autofixes use `typing_extensions` on Python 3.8, // since `typing.Annotated` was added in Python 3.9 #[test_case(Rule::FastApiNonAnnotatedDependency, Path::new("FAST002_0.py"))] @@ -34,9 +60,9 @@ mod tests { let snapshot = format!("{}_{}_py38", rule_code.name(), path.to_string_lossy()); let diagnostics = test_path( Path::new("fastapi").join(path).as_path(), - &settings::LinterSettings { - unresolved_target_version: ruff_python_ast::PythonVersion::PY38.into(), - ..settings::LinterSettings::for_rule(rule_code) + &LinterSettings { + unresolved_target_version: PythonVersion::PY38.into(), + ..LinterSettings::for_rule(rule_code) }, )?; assert_diagnostics!(snapshot, diagnostics); diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap new file mode 100644 index 0000000000000..fc36a819d1453 --- /dev/null +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap @@ -0,0 +1,213 @@ +--- +source: crates/ruff_linter/src/rules/fastapi/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 10 +Added: 0 + +--- Removed --- +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:17:22 + | +17 | @app.post("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +18 | async def create_item(item: Item) -> Item: +19 | return item + | +help: Remove argument +14 | # Errors +15 | +16 | + - @app.post("/items/", response_model=Item) +17 + @app.post("/items/") +18 | async def create_item(item: Item) -> Item: +19 | return item +20 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:22:22 + | +22 | @app.post("/items/", response_model=list[Item]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +23 | async def create_item(item: Item) -> list[Item]: +24 | return item + | +help: Remove argument +19 | return item +20 | +21 | + - @app.post("/items/", response_model=list[Item]) +22 + @app.post("/items/") +23 | async def create_item(item: Item) -> list[Item]: +24 | return item +25 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:27:22 + | +27 | @app.post("/items/", response_model=List[Item]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +28 | async def create_item(item: Item) -> List[Item]: +29 | return item + | +help: Remove argument +24 | return item +25 | +26 | + - @app.post("/items/", response_model=List[Item]) +27 + @app.post("/items/") +28 | async def create_item(item: Item) -> List[Item]: +29 | return item +30 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:32:22 + | +32 | @app.post("/items/", response_model=Dict[str, Item]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +33 | async def create_item(item: Item) -> Dict[str, Item]: +34 | return item + | +help: Remove argument +29 | return item +30 | +31 | + - @app.post("/items/", response_model=Dict[str, Item]) +32 + @app.post("/items/") +33 | async def create_item(item: Item) -> Dict[str, Item]: +34 | return item +35 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:37:22 + | +37 | @app.post("/items/", response_model=str) + | ^^^^^^^^^^^^^^^^^^ +38 | async def create_item(item: Item) -> str: +39 | return item + | +help: Remove argument +34 | return item +35 | +36 | + - @app.post("/items/", response_model=str) +37 + @app.post("/items/") +38 | async def create_item(item: Item) -> str: +39 | return item +40 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:42:21 + | +42 | @app.get("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +43 | async def create_item(item: Item) -> Item: +44 | return item + | +help: Remove argument +39 | return item +40 | +41 | + - @app.get("/items/", response_model=Item) +42 + @app.get("/items/") +43 | async def create_item(item: Item) -> Item: +44 | return item +45 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:47:21 + | +47 | @app.get("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +48 | @app.post("/items/", response_model=Item) +49 | async def create_item(item: Item) -> Item: + | +help: Remove argument +44 | return item +45 | +46 | + - @app.get("/items/", response_model=Item) +47 + @app.get("/items/") +48 | @app.post("/items/", response_model=Item) +49 | async def create_item(item: Item) -> Item: +50 | return item +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:48:22 + | +47 | @app.get("/items/", response_model=Item) +48 | @app.post("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +49 | async def create_item(item: Item) -> Item: +50 | return item + | +help: Remove argument +45 | +46 | +47 | @app.get("/items/", response_model=Item) + - @app.post("/items/", response_model=Item) +48 + @app.post("/items/") +49 | async def create_item(item: Item) -> Item: +50 | return item +51 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:53:24 + | +53 | @router.get("/items/", response_model=Item) + | ^^^^^^^^^^^^^^^^^^^ +54 | async def create_item(item: Item) -> Item: +55 | return item + | +help: Remove argument +50 | return item +51 | +52 | + - @router.get("/items/", response_model=Item) +53 + @router.get("/items/") +54 | async def create_item(item: Item) -> Item: +55 | return item +56 | +note: This is an unsafe fix and may change runtime behavior + + +FAST001 [*] FastAPI route with redundant `response_model` argument + --> FAST001.py:118:23 + | +116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: +117 | # Error +118 | @app_arg.get("/", response_model=str) + | ^^^^^^^^^^^^^^^^^^ +119 | async def get_root() -> str: +120 | return "Hello World!" + | +help: Remove argument +115 | +116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: +117 | # Error + - @app_arg.get("/", response_model=str) +118 + @app_arg.get("/") +119 | async def get_root() -> str: +120 | return "Hello World!" +121 | +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap new file mode 100644 index 0000000000000..2feae227dbade --- /dev/null +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap @@ -0,0 +1,74 @@ +--- +source: crates/ruff_linter/src/rules/fastapi/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 3 +Added: 0 + +--- Removed --- +FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature + --> FAST003.py:158:19 + | +157 | ### Errors +158 | @app.get("/things/{thing_id}") + | ^^^^^^^^^^ +159 | async def single(other: Annotated[str, Depends(something_else)]): ... +160 | @app.get("/things/{thing_id}") + | +help: Add `thing_id` to function signature +156 | +157 | ### Errors +158 | @app.get("/things/{thing_id}") + - async def single(other: Annotated[str, Depends(something_else)]): ... +159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ... +160 | @app.get("/things/{thing_id}") +161 | async def default(other: str = Depends(something_else)): ... +162 | +note: This is an unsafe fix and may change runtime behavior + + +FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature + --> FAST003.py:197:12 + | +196 | # Errors +197 | @app.get("/{id}") + | ^^^^ +198 | async def get_id_pydantic_full( +199 | params: Annotated[PydanticParams, Depends(PydanticParams)], + | +help: Add `id` to function signature +196 | # Errors +197 | @app.get("/{id}") +198 | async def get_id_pydantic_full( + - params: Annotated[PydanticParams, Depends(PydanticParams)], +199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id, +200 | ): ... +201 | @app.get("/{id}") +202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... +note: This is an unsafe fix and may change runtime behavior + + +FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature + --> FAST003.py:201:12 + | +199 | params: Annotated[PydanticParams, Depends(PydanticParams)], +200 | ): ... +201 | @app.get("/{id}") + | ^^^^ +202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... +203 | @app.get("/{id}") + | +help: Add `id` to function signature +199 | params: Annotated[PydanticParams, Depends(PydanticParams)], +200 | ): ... +201 | @app.get("/{id}") + - async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... +202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ... +203 | @app.get("/{id}") +204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ... +205 | +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap index f1c5682ef4958..e3843a16cd064 100644 --- a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-redundant-response-model_FAST001.py.snap @@ -1,195 +1,4 @@ --- source: crates/ruff_linter/src/rules/fastapi/mod.rs --- -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:17:22 - | -17 | @app.post("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -18 | async def create_item(item: Item) -> Item: -19 | return item - | -help: Remove argument -14 | # Errors -15 | -16 | - - @app.post("/items/", response_model=Item) -17 + @app.post("/items/") -18 | async def create_item(item: Item) -> Item: -19 | return item -20 | -note: This is an unsafe fix and may change runtime behavior -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:22:22 - | -22 | @app.post("/items/", response_model=list[Item]) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -23 | async def create_item(item: Item) -> list[Item]: -24 | return item - | -help: Remove argument -19 | return item -20 | -21 | - - @app.post("/items/", response_model=list[Item]) -22 + @app.post("/items/") -23 | async def create_item(item: Item) -> list[Item]: -24 | return item -25 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:27:22 - | -27 | @app.post("/items/", response_model=List[Item]) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -28 | async def create_item(item: Item) -> List[Item]: -29 | return item - | -help: Remove argument -24 | return item -25 | -26 | - - @app.post("/items/", response_model=List[Item]) -27 + @app.post("/items/") -28 | async def create_item(item: Item) -> List[Item]: -29 | return item -30 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:32:22 - | -32 | @app.post("/items/", response_model=Dict[str, Item]) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -33 | async def create_item(item: Item) -> Dict[str, Item]: -34 | return item - | -help: Remove argument -29 | return item -30 | -31 | - - @app.post("/items/", response_model=Dict[str, Item]) -32 + @app.post("/items/") -33 | async def create_item(item: Item) -> Dict[str, Item]: -34 | return item -35 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:37:22 - | -37 | @app.post("/items/", response_model=str) - | ^^^^^^^^^^^^^^^^^^ -38 | async def create_item(item: Item) -> str: -39 | return item - | -help: Remove argument -34 | return item -35 | -36 | - - @app.post("/items/", response_model=str) -37 + @app.post("/items/") -38 | async def create_item(item: Item) -> str: -39 | return item -40 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:42:21 - | -42 | @app.get("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -43 | async def create_item(item: Item) -> Item: -44 | return item - | -help: Remove argument -39 | return item -40 | -41 | - - @app.get("/items/", response_model=Item) -42 + @app.get("/items/") -43 | async def create_item(item: Item) -> Item: -44 | return item -45 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:47:21 - | -47 | @app.get("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -48 | @app.post("/items/", response_model=Item) -49 | async def create_item(item: Item) -> Item: - | -help: Remove argument -44 | return item -45 | -46 | - - @app.get("/items/", response_model=Item) -47 + @app.get("/items/") -48 | @app.post("/items/", response_model=Item) -49 | async def create_item(item: Item) -> Item: -50 | return item -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:48:22 - | -47 | @app.get("/items/", response_model=Item) -48 | @app.post("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -49 | async def create_item(item: Item) -> Item: -50 | return item - | -help: Remove argument -45 | -46 | -47 | @app.get("/items/", response_model=Item) - - @app.post("/items/", response_model=Item) -48 + @app.post("/items/") -49 | async def create_item(item: Item) -> Item: -50 | return item -51 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:53:24 - | -53 | @router.get("/items/", response_model=Item) - | ^^^^^^^^^^^^^^^^^^^ -54 | async def create_item(item: Item) -> Item: -55 | return item - | -help: Remove argument -50 | return item -51 | -52 | - - @router.get("/items/", response_model=Item) -53 + @router.get("/items/") -54 | async def create_item(item: Item) -> Item: -55 | return item -56 | -note: This is an unsafe fix and may change runtime behavior - -FAST001 [*] FastAPI route with redundant `response_model` argument - --> FAST001.py:118:23 - | -116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: -117 | # Error -118 | @app_arg.get("/", response_model=str) - | ^^^^^^^^^^^^^^^^^^ -119 | async def get_root() -> str: -120 | return "Hello World!" - | -help: Remove argument -115 | -116 | def setup_app(app_arg: FastAPI, non_app: str) -> None: -117 | # Error - - @app_arg.get("/", response_model=str) -118 + @app_arg.get("/") -119 | async def get_root() -> str: -120 | return "Hello World!" -121 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap index a3ea2bf7cebe0..fb6bde76f89e3 100644 --- a/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap +++ b/crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__fast-api-unused-path-parameter_FAST003.py.snap @@ -324,26 +324,6 @@ help: Add `name` to function signature 91 | note: This is an unsafe fix and may change runtime behavior -FAST003 [*] Parameter `thing_id` appears in route path, but not in `single` signature - --> FAST003.py:158:19 - | -157 | ### Errors -158 | @app.get("/things/{thing_id}") - | ^^^^^^^^^^ -159 | async def single(other: Annotated[str, Depends(something_else)]): ... -160 | @app.get("/things/{thing_id}") - | -help: Add `thing_id` to function signature -156 | -157 | ### Errors -158 | @app.get("/things/{thing_id}") - - async def single(other: Annotated[str, Depends(something_else)]): ... -159 + async def single(other: Annotated[str, Depends(something_else)], thing_id): ... -160 | @app.get("/things/{thing_id}") -161 | async def default(other: str = Depends(something_else)): ... -162 | -note: This is an unsafe fix and may change runtime behavior - FAST003 [*] Parameter `thing_id` appears in route path, but not in `default` signature --> FAST003.py:160:19 | @@ -364,47 +344,6 @@ help: Add `thing_id` to function signature 164 | ### No errors note: This is an unsafe fix and may change runtime behavior -FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_full` signature - --> FAST003.py:197:12 - | -196 | # Errors -197 | @app.get("/{id}") - | ^^^^ -198 | async def get_id_pydantic_full( -199 | params: Annotated[PydanticParams, Depends(PydanticParams)], - | -help: Add `id` to function signature -196 | # Errors -197 | @app.get("/{id}") -198 | async def get_id_pydantic_full( - - params: Annotated[PydanticParams, Depends(PydanticParams)], -199 + params: Annotated[PydanticParams, Depends(PydanticParams)], id, -200 | ): ... -201 | @app.get("/{id}") -202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... -note: This is an unsafe fix and may change runtime behavior - -FAST003 [*] Parameter `id` appears in route path, but not in `get_id_pydantic_short` signature - --> FAST003.py:201:12 - | -199 | params: Annotated[PydanticParams, Depends(PydanticParams)], -200 | ): ... -201 | @app.get("/{id}") - | ^^^^ -202 | async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... -203 | @app.get("/{id}") - | -help: Add `id` to function signature -199 | params: Annotated[PydanticParams, Depends(PydanticParams)], -200 | ): ... -201 | @app.get("/{id}") - - async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()]): ... -202 + async def get_id_pydantic_short(params: Annotated[PydanticParams, Depends()], id): ... -203 | @app.get("/{id}") -204 | async def get_id_init_not_annotated(params = Depends(InitParams)): ... -205 | -note: This is an unsafe fix and may change runtime behavior - FAST003 [*] Parameter `id` appears in route path, but not in `get_id_init_not_annotated` signature --> FAST003.py:203:12 | diff --git a/crates/ruff_linter/src/rules/flake8_builtins/mod.rs b/crates/ruff_linter/src/rules/flake8_builtins/mod.rs index 315ab704e72ad..0ab2764765fb9 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_builtins/mod.rs @@ -10,12 +10,12 @@ mod tests { use anyhow::Result; use test_case::test_case; - use crate::assert_diagnostics; use crate::registry::Rule; use crate::rules::flake8_builtins; use crate::settings::LinterSettings; use crate::settings::types::PreviewMode; use crate::test::{test_path, test_resource_path}; + use crate::{assert_diagnostics, assert_diagnostics_diff}; use ruff_python_ast::PythonVersion; #[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))] @@ -64,6 +64,28 @@ mod tests { Ok(()) } + #[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))] + fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "deferred_annotations_diff_{}_{}", + rule_code.name(), + path.to_string_lossy() + ); + assert_diagnostics_diff!( + snapshot, + Path::new("flake8_builtins").join(path).as_path(), + &LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + ..LinterSettings::for_rule(rule_code) + }, + &LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + ..LinterSettings::for_rule(rule_code) + }, + ); + Ok(()) + } + #[test_case(Rule::BuiltinAttributeShadowing, Path::new("A003.py"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!( diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap index 1444cc7d6a914..df35fcb66a979 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py.snap @@ -1,22 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs --- -A003 Python builtin is shadowed by method `str` from line 14 - --> A003.py:17:31 - | -15 | pass -16 | -17 | def method_usage(self) -> str: - | ^^^ -18 | pass - | -A003 Python builtin is shadowed by class attribute `id` from line 3 - --> A003.py:20:34 - | -18 | pass -19 | -20 | def attribute_usage(self) -> id: - | ^^ -21 | pass - | diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap index ad43e1b0fccb3..df35fcb66a979 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__A003_A003.py_builtins_ignorelist.snap @@ -1,12 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs --- -A003 Python builtin is shadowed by method `str` from line 14 - --> A003.py:17:31 - | -15 | pass -16 | -17 | def method_usage(self) -> str: - | ^^^ -18 | pass - | + diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap new file mode 100644 index 0000000000000..ed283ed65a9e1 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap @@ -0,0 +1,32 @@ +--- +source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 2 +Added: 0 + +--- Removed --- +A003 Python builtin is shadowed by method `str` from line 14 + --> A003.py:17:31 + | +15 | pass +16 | +17 | def method_usage(self) -> str: + | ^^^ +18 | pass + | + + +A003 Python builtin is shadowed by class attribute `id` from line 3 + --> A003.py:20:34 + | +18 | pass +19 | +20 | def attribute_usage(self) -> id: + | ^^ +21 | pass + | diff --git a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap index 6fd8c6f96104c..537a64433c920 100644 --- a/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap +++ b/crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__preview__A003_A003.py.snap @@ -1,26 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_builtins/mod.rs --- -A003 Python builtin is shadowed by method `str` from line 14 - --> A003.py:17:31 - | -15 | pass -16 | -17 | def method_usage(self) -> str: - | ^^^ -18 | pass - | - -A003 Python builtin is shadowed by class attribute `id` from line 3 - --> A003.py:20:34 - | -18 | pass -19 | -20 | def attribute_usage(self) -> id: - | ^^ -21 | pass - | - A003 Python builtin is shadowed by method `property` from line 26 --> A003.py:31:7 | diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap index 15a50a1e1bf4b..8192743812e87 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap @@ -1,6 +1,196 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`. + --> PYI055.py:4:4 + | +2 | from typing import Union +3 | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] + | +help: Combine multiple `type` members +1 | import builtins +2 | from typing import Union +3 | + - s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +4 + s: type[int | str | complex] +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | float]`. + --> PYI055.py:5:4 + | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + | +help: Combine multiple `type` members +2 | from typing import Union +3 | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] + - t: type[int] | type[str] | type[float] +5 + t: type[int | str | float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`. + --> PYI055.py:6:4 + | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] + | +help: Combine multiple `type` members +3 | +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] + - u: builtins.type[int] | type[str] | builtins.type[complex] +6 + u: type[int | str | complex] +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`. + --> PYI055.py:7:4 + | +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] + | +help: Combine multiple `type` members +4 | s: builtins.type[int] | builtins.type[str] | builtins.type[complex] +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] + - v: Union[type[float], type[complex]] +7 + v: type[Union[float, complex]] +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float | int, complex]]`. + --> PYI055.py:8:4 + | + 6 | u: builtins.type[int] | type[str] | builtins.type[complex] + 7 | v: Union[type[float], type[complex]] + 8 | w: Union[type[float | int], type[complex]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + | +help: Combine multiple `type` members +5 | t: type[int] | type[str] | type[float] +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] + - w: Union[type[float | int], type[complex]] +8 + w: type[Union[float | int, complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[Union[float, int], complex]]`. + --> PYI055.py:9:4 + | + 7 | v: Union[type[float], type[complex]] + 8 | w: Union[type[float | int], type[complex]] + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | +help: Combine multiple `type` members +6 | u: builtins.type[int] | type[str] | builtins.type[complex] +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] + - x: Union[Union[type[Union[float, int]], type[complex]]] +9 + x: type[Union[Union[float, int], complex]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +12 | + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float | int, complex]]`. + --> PYI055.py:10:4 + | + 8 | w: Union[type[float | int], type[complex]] + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | +help: Combine multiple `type` members +7 | v: Union[type[float], type[complex]] +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] + - y: Union[Union[Union[type[float | int], type[complex]]]] +10 + y: type[Union[float | int, complex]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +12 | +13 | + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[complex, Union[float, int]]]`. + --> PYI055.py:11:4 + | + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Combine multiple `type` members +8 | w: Union[type[float | int], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[float | int], type[complex]]]] + - z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +11 + z: type[Union[complex, Union[float, int]]] +12 | +13 | +14 | def func(arg: type[int] | str | type[float]) -> None: + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`. + --> PYI055.py:14:15 + | +14 | def func(arg: type[int] | str | type[float]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +15 | ... + | +help: Combine multiple `type` members +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] +12 | +13 | + - def func(arg: type[int] | str | type[float]) -> None: +14 + def func(arg: type[int | float] | str) -> None: +15 | ... +16 | +17 | + +PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`. + --> PYI055.py:29:7 + | +28 | # OK +29 | item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: Combine multiple `type` members +26 | +27 | +28 | # OK + - item: type[requests_mock.Mocker] | type[httpretty] = requests_mock.Mocker +29 + item: type[requests_mock.Mocker | httpretty] = requests_mock.Mocker +30 | +31 | +32 | def func(): + PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty | str]`. --> PYI055.py:34:8 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap index ca004802ade40..2d3e377e1ff65 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001-TC002-TC003_TC001-3_future.py.snap @@ -11,18 +11,17 @@ TC003 [*] Move standard library import `collections.Counter` into a type-checkin | help: Move into type-checking block - from collections import Counter -1 + from __future__ import annotations -2 | -3 | from elsewhere import third_party -4 | -5 | from . import first_party -6 + from typing import TYPE_CHECKING -7 + -8 + if TYPE_CHECKING: -9 + from collections import Counter +1 | +2 | from elsewhere import third_party +3 | +4 | from . import first_party +5 + from typing import TYPE_CHECKING +6 + +7 + if TYPE_CHECKING: +8 + from collections import Counter +9 | 10 | -11 | -12 | def f(x: first_party.foo): ... +11 | def f(x: first_party.foo): ... note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `elsewhere.third_party` into a type-checking block @@ -36,19 +35,18 @@ TC002 [*] Move third-party import `elsewhere.third_party` into a type-checking b 5 | from . import first_party | help: Move into type-checking block -1 + from __future__ import annotations -2 | from collections import Counter -3 | +1 | from collections import Counter +2 | - from elsewhere import third_party -4 | -5 | from . import first_party -6 + from typing import TYPE_CHECKING -7 + -8 + if TYPE_CHECKING: -9 + from elsewhere import third_party +3 | +4 | from . import first_party +5 + from typing import TYPE_CHECKING +6 + +7 + if TYPE_CHECKING: +8 + from elsewhere import third_party +9 | 10 | -11 | -12 | def f(x: first_party.foo): ... +11 | def f(x: first_party.foo): ... note: This is an unsafe fix and may change runtime behavior TC001 [*] Move application import `.first_party` into a type-checking block @@ -60,17 +58,15 @@ TC001 [*] Move application import `.first_party` into a type-checking block | ^^^^^^^^^^^ | help: Move into type-checking block -1 + from __future__ import annotations -2 | from collections import Counter -3 | -4 | from elsewhere import third_party -5 | +2 | +3 | from elsewhere import third_party +4 | - from . import first_party -6 + from typing import TYPE_CHECKING -7 + -8 + if TYPE_CHECKING: -9 + from . import first_party +5 + from typing import TYPE_CHECKING +6 + +7 + if TYPE_CHECKING: +8 + from . import first_party +9 | 10 | -11 | -12 | def f(x: first_party.foo): ... +11 | def f(x: first_party.foo): ... note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap index 6d579e57c211b..12b5aa40f2e06 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC001_TC001_future.py.snap @@ -12,15 +12,14 @@ TC001 [*] Move application import `.first_party` into a type-checking block | help: Move into type-checking block - def f(): -1 + from __future__ import annotations -2 + from typing import TYPE_CHECKING -3 + -4 + if TYPE_CHECKING: -5 | from . import first_party -6 + def f(): -7 | -8 | def f(x: first_party.foo): ... -9 | +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from . import first_party +5 + def f(): +6 | +7 | def f(x: first_party.foo): ... +8 | note: This is an unsafe fix and may change runtime behavior TC001 [*] Move application import `.foo` into a type-checking block @@ -33,24 +32,19 @@ TC001 [*] Move application import `.foo` into a type-checking block 59 | def f(x: Union[foo.Ty, int]): ... | help: Move into type-checking block -1 + from __future__ import annotations -2 | def f(): -3 | from . import first_party -4 | --------------------------------------------------------------------------------- +50 | 51 | -52 | -53 | # unions +52 | # unions - from typing import Union -54 + from typing import Union, TYPE_CHECKING -55 | -56 + if TYPE_CHECKING: -57 + from . import foo -58 + -59 | -60 | def n(): +53 + from typing import Union, TYPE_CHECKING +54 + +55 + if TYPE_CHECKING: +56 + from . import foo +57 | +58 | +59 | def n(): - from . import foo -61 | -62 | def f(x: Union[foo.Ty, int]): ... -63 | def g(x: foo.Ty | int): ... +60 | +61 | def f(x: Union[foo.Ty, int]): ... +62 | def g(x: foo.Ty | int): ... note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap index 2d3f3068076ad..d84c1fff21d2e 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__add_future_import__TC002_TC002.py.snap @@ -220,32 +220,3 @@ help: Move into type-checking block 52 | x = dict["pd.DataFrame", "pd.DataFrame"] 53 | note: This is an unsafe fix and may change runtime behavior - -TC002 [*] Move third-party import `module.Member` into a type-checking block - --> TC002.py:172:24 - | -170 | global Member -171 | -172 | from module import Member - | ^^^^^^ -173 | -174 | x: Member = 1 - | -help: Move into type-checking block -1 | """Tests to determine accurate detection of typing-only imports.""" -2 + from typing import TYPE_CHECKING -3 + -4 + if TYPE_CHECKING: -5 + from module import Member -6 | -7 | -8 | def f(): --------------------------------------------------------------------------------- -173 | def f(): -174 | global Member -175 | - - from module import Member -176 | -177 | x: Member = 1 -178 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap index 1e6dc27a29b85..22bae5d08e6d2 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_runtime-import-in-type-checking-block_quote.py.snap @@ -1,26 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Quote references to `pandas.DataFrame`. Import is in a type-checking block. - --> quote.py:57:28 - | -56 | if TYPE_CHECKING: -57 | from pandas import DataFrame - | ^^^^^^^^^ -58 | -59 | def func(value: DataFrame): - | -help: Quote references -56 | if TYPE_CHECKING: -57 | from pandas import DataFrame -58 | - - def func(value: DataFrame): -59 + def func(value: "DataFrame"): -60 | ... -61 | -62 | -note: This is an unsafe fix and may change runtime behavior - TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting. --> quote.py:110:28 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap index beccb0c7d9aa3..8efb644122114 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote.py.snap @@ -11,18 +11,15 @@ TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block 4 | def baz() -> DataFrame: | help: Move into type-checking block - - def f(): -1 + from typing import TYPE_CHECKING -2 + -3 + if TYPE_CHECKING: -4 | from pandas import DataFrame -5 + def f(): -6 | - - def baz() -> DataFrame: -7 + def baz() -> "DataFrame": -8 | ... -9 | -10 | + - def f(): +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from pandas import DataFrame +5 + def f(): +6 | +7 | def baz() -> DataFrame: +8 | ... note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -48,11 +45,8 @@ help: Move into type-checking block 12 | def f(): - from pandas import DataFrame 13 | - - def baz() -> DataFrame[int]: -14 + def baz() -> "DataFrame[int]": +14 | def baz() -> DataFrame[int]: 15 | ... -16 | -17 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas` into a type-checking block @@ -78,11 +72,8 @@ help: Move into type-checking block 19 | def f(): - import pandas as pd 20 | - - def baz() -> pd.DataFrame: -21 + def baz() -> "pd.DataFrame": +21 | def baz() -> pd.DataFrame: 22 | ... -23 | -24 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas` into a type-checking block @@ -108,11 +99,8 @@ help: Move into type-checking block 26 | def f(): - import pandas as pd 27 | - - def baz() -> pd.DataFrame.Extra: -28 + def baz() -> "pd.DataFrame.Extra": +28 | def baz() -> pd.DataFrame.Extra: 29 | ... -30 | -31 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas` into a type-checking block @@ -138,11 +126,8 @@ help: Move into type-checking block 33 | def f(): - import pandas as pd 34 | - - def baz() -> pd.DataFrame | int: -35 + def baz() -> "pd.DataFrame | int": +35 | def baz() -> pd.DataFrame | int: 36 | ... -37 | -38 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -168,11 +153,8 @@ help: Move into type-checking block 41 | def f(): - from pandas import DataFrame 42 | - - def baz() -> DataFrame(): -43 + def baz() -> "DataFrame()": +43 | def baz() -> DataFrame(): 44 | ... -45 | -46 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -199,11 +181,8 @@ help: Move into type-checking block 50 | - from pandas import DataFrame 51 | - - def baz() -> DataFrame[Literal["int"]]: -52 + def baz() -> "DataFrame[Literal['int']]": +52 | def baz() -> DataFrame[Literal["int"]]: 53 | ... -54 | -55 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -229,11 +208,8 @@ help: Move into type-checking block 67 | def f(): - from pandas import DataFrame, Series 68 | - - def baz() -> DataFrame | Series: -69 + def baz() -> "DataFrame | Series": +69 | def baz() -> DataFrame | Series: 70 | ... -71 | -72 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.Series` into a type-checking block @@ -259,11 +235,8 @@ help: Move into type-checking block 67 | def f(): - from pandas import DataFrame, Series 68 | - - def baz() -> DataFrame | Series: -69 + def baz() -> "DataFrame | Series": +69 | def baz() -> DataFrame | Series: 70 | ... -71 | -72 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -290,23 +263,7 @@ help: Move into type-checking block - from pandas import DataFrame, Series 75 | 76 | def baz() -> ( - - DataFrame | - - Series -77 + "DataFrame | Series" -78 | ): -79 | ... -80 | -81 | class C: - - x: DataFrame[ - - int - - ] = 1 -82 + x: "DataFrame[int]" = 1 -83 | - - def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: -84 + def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]": -85 | ... -86 | -87 | +77 | DataFrame | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.Series` into a type-checking block @@ -333,23 +290,7 @@ help: Move into type-checking block - from pandas import DataFrame, Series 75 | 76 | def baz() -> ( - - DataFrame | - - Series -77 + "DataFrame | Series" -78 | ): -79 | ... -80 | -81 | class C: - - x: DataFrame[ - - int - - ] = 1 -82 + x: "DataFrame[int]" = 1 -83 | - - def func() -> DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]: -84 + def func() -> "DataFrame[[DataFrame[_P, _R]], DataFrame[_P, _R]]": -85 | ... -86 | -87 | +77 | DataFrame | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block @@ -375,11 +316,8 @@ help: Move into type-checking block 92 | def f(): - from pandas import DataFrame, Series 93 | - - def func(self) -> DataFrame | list[Series]: -94 + def func(self) -> "DataFrame | list[Series]": +94 | def func(self) -> DataFrame | list[Series]: 95 | pass -96 | -97 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `pandas.Series` into a type-checking block @@ -405,9 +343,6 @@ help: Move into type-checking block 92 | def f(): - from pandas import DataFrame, Series 93 | - - def func(self) -> DataFrame | list[Series]: -94 + def func(self) -> "DataFrame | list[Series]": +94 | def func(self) -> DataFrame | list[Series]: 95 | pass -96 | -97 | note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap index 0779cfdfd68ba..5e0b5862ab53f 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote2.py.snap @@ -11,18 +11,15 @@ TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` 4 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]): | help: Move into type-checking block - - def f(): -1 + from typing import TYPE_CHECKING -2 + -3 + if TYPE_CHECKING: -4 | from django.contrib.auth.models import AbstractBaseUser -5 + def f(): -6 | - - def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]): -7 + def test_remove_inner_quotes_double(self, user: "AbstractBaseUser[int]"): -8 | pass -9 | -10 | + - def f(): +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from django.contrib.auth.models import AbstractBaseUser +5 + def f(): +6 | +7 | def test_remove_inner_quotes_double(self, user: AbstractBaseUser["int"]): +8 | pass note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -48,11 +45,8 @@ help: Move into type-checking block 12 | def f(): - from django.contrib.auth.models import AbstractBaseUser 13 | - - def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']): -14 + def test_remove_inner_quotes_single(self, user: "AbstractBaseUser[int]"): +14 | def test_remove_inner_quotes_single(self, user: AbstractBaseUser['int']): 15 | pass -16 | -17 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -78,11 +72,8 @@ help: Move into type-checking block 19 | def f(): - from django.contrib.auth.models import AbstractBaseUser 20 | - - def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]): -21 + def test_remove_inner_quotes_mixed(self, user: "AbstractBaseUser[int, str]"): +21 | def test_remove_inner_quotes_mixed(self, user: AbstractBaseUser['int', "str"]): 22 | pass -23 | -24 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -109,11 +100,8 @@ help: Move into type-checking block 28 | - from django.contrib.auth.models import AbstractBaseUser 29 | - - def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]): -30 + def test_literal_annotation_args_contain_quotes(self, type1: "AbstractBaseUser[Literal['user', 'admin'], Annotated[int, '1', 2]]"): +30 | def test_literal_annotation_args_contain_quotes(self, type1: AbstractBaseUser[Literal["user", "admin"], Annotated["int", "1", 2]]): 31 | pass -32 | -33 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -140,11 +128,8 @@ help: Move into type-checking block 37 | - from django.contrib.auth.models import AbstractBaseUser 38 | - - def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]): -39 + def test_union_contain_inner_quotes(self, type1: "AbstractBaseUser[int | Literal['int']]"): +39 | def test_union_contain_inner_quotes(self, type1: AbstractBaseUser["int" | Literal["int"]]): 40 | pass -41 | -42 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -171,11 +156,8 @@ help: Move into type-checking block 46 | - from django.contrib.auth.models import AbstractBaseUser 47 | - - def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]): -48 + def test_inner_literal_mixed_quotes(user: "AbstractBaseUser[Literal['user', 'admin']]"): +48 | def test_inner_literal_mixed_quotes(user: AbstractBaseUser[Literal['user', "admin"]]): 49 | pass -50 | -51 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -202,11 +184,8 @@ help: Move into type-checking block 55 | - from django.contrib.auth.models import AbstractBaseUser 56 | - - def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]): -57 + def test_inner_literal_single_quote(user: "AbstractBaseUser[Literal['int'], str]"): +57 | def test_inner_literal_single_quote(user: AbstractBaseUser[Literal['int'], str]): 58 | pass -59 | -60 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -233,11 +212,8 @@ help: Move into type-checking block 64 | - from django.contrib.auth.models import AbstractBaseUser 65 | - - def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]): -66 + def test_mixed_quotes_literal(user: "AbstractBaseUser[Literal['user'], int]"): +66 | def test_mixed_quotes_literal(user: AbstractBaseUser[Literal['user'], "int"]): 67 | pass -68 | -69 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -264,9 +240,6 @@ help: Move into type-checking block 73 | - from django.contrib.auth.models import AbstractBaseUser 74 | - - def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]): -75 + def test_annotated_literal_mixed_quotes(user: "AbstractBaseUser[Annotated[str, 'max_length=20', Literal['user', 'admin']]]"): +75 | def test_annotated_literal_mixed_quotes(user: AbstractBaseUser[Annotated[str, "max_length=20", Literal['user', "admin"]]]): 76 | pass -77 | -78 | note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap index 4614aecb8952f..5877229b63318 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__quote_typing-only-third-party-import_quote3.py.snap @@ -21,11 +21,8 @@ help: Move into type-checking block 7 | - from django.contrib.auth.models import AbstractBaseUser 8 | - - def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]): -9 + def test_union_literal_mixed_quotes(user: 'AbstractBaseUser[Union[Literal["active", "inactive"], str]]'): +9 | def test_union_literal_mixed_quotes(user: AbstractBaseUser[Union[Literal['active', "inactive"], str]]): 10 | pass -11 | -12 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -52,11 +49,8 @@ help: Move into type-checking block 16 | - from django.contrib.auth.models import AbstractBaseUser 17 | - - def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]): -18 + def test_callable_literal_mixed_quotes(callable_fn: 'AbstractBaseUser[Callable[[int, Literal["admin", "user"]], bool]]'): +18 | def test_callable_literal_mixed_quotes(callable_fn: AbstractBaseUser[Callable[["int", Literal['admin', "user"]], 'bool']]): 19 | pass -20 | -21 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models.AbstractBaseUser` into a type-checking block @@ -83,11 +77,8 @@ help: Move into type-checking block 25 | - from django.contrib.auth.models import AbstractBaseUser 26 | - - def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]): -27 + def test_callable_annotated_literal(callable_fn: 'AbstractBaseUser[Callable[[int, Annotated[str, Literal["active", "inactive"]]], bool]]'): +27 | def test_callable_annotated_literal(callable_fn: AbstractBaseUser[Callable[[int, Annotated[str, Literal['active', "inactive"]]], bool]]): 28 | pass -29 | -30 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block @@ -114,11 +105,8 @@ help: Move into type-checking block 34 | - from django.contrib.auth import models 35 | - - def test_attribute(arg: models.AbstractBaseUser["int"]): -36 + def test_attribute(arg: 'models.AbstractBaseUser[int]'): +36 | def test_attribute(arg: models.AbstractBaseUser["int"]): 37 | pass -38 | -39 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `django.contrib.auth.models` into a type-checking block @@ -145,11 +133,8 @@ help: Move into type-checking block 43 | - from django.contrib.auth import models 44 | - - def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]): -45 + def test_attribute_typing_literal(arg: 'models.AbstractBaseUser[Literal["admin"]]'): +45 | def test_attribute_typing_literal(arg: models.AbstractBaseUser[Literal["admin"]]): 46 | pass -47 | -48 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `third_party.Type` into a type-checking block @@ -176,11 +161,8 @@ help: Move into type-checking block 62 | from typing import Literal - from third_party import Type 63 | - - def test_string_contains_opposite_quote(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]): -64 + def test_string_contains_opposite_quote(self, type1: 'Type[Literal["\'"]]', type2: 'Type[Literal["\'"]]'): +64 | def test_string_contains_opposite_quote(self, type1: Type[Literal["'"]], type2: Type[Literal["\'"]]): 65 | pass -66 | -67 | note: This is an unsafe fix and may change runtime behavior TC002 [*] Move third-party import `third_party.Type` into a type-checking block @@ -207,7 +189,6 @@ help: Move into type-checking block 70 | from typing import Literal - from third_party import Type 71 | - - def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]): -72 + def test_quote_contains_backslash(self, type1: 'Type[Literal["\\n"]]', type2: 'Type[Literal[\'"\']]'): +72 | def test_quote_contains_backslash(self, type1: Type[Literal["\n"]], type2: Type[Literal["\""]]): 73 | pass note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap index fc6eb83923964..6c5ead27428ce 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_4.py.snap @@ -1,21 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `typing.Any` out of type-checking block. Import is used for more than type hinting. - --> TC004_4.py:4:24 - | -3 | if TYPE_CHECKING: -4 | from typing import Any - | ^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING, Type -2 + from typing import Any -3 | -4 | if TYPE_CHECKING: - - from typing import Any -5 + pass -6 | -7 | -8 | def example(*args: Any, **kwargs: Any): -note: This is an unsafe fix and may change runtime behavior + diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap index 98817b85bdb7c..6c5ead27428ce 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_5.py.snap @@ -1,59 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `typing.List` out of type-checking block. Import is used for more than type hinting. - --> TC004_5.py:4:24 - | -3 | if TYPE_CHECKING: -4 | from typing import List, Sequence, Set - | ^^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import List, Sequence, Set -3 | -4 | if TYPE_CHECKING: - - from typing import List, Sequence, Set -5 + pass -6 | -7 | -8 | def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): -note: This is an unsafe fix and may change runtime behavior -TC004 [*] Move import `typing.Sequence` out of type-checking block. Import is used for more than type hinting. - --> TC004_5.py:4:30 - | -3 | if TYPE_CHECKING: -4 | from typing import List, Sequence, Set - | ^^^^^^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import List, Sequence, Set -3 | -4 | if TYPE_CHECKING: - - from typing import List, Sequence, Set -5 + pass -6 | -7 | -8 | def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): -note: This is an unsafe fix and may change runtime behavior - -TC004 [*] Move import `typing.Set` out of type-checking block. Import is used for more than type hinting. - --> TC004_5.py:4:40 - | -3 | if TYPE_CHECKING: -4 | from typing import List, Sequence, Set - | ^^^ - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import List, Sequence, Set -3 | -4 | if TYPE_CHECKING: - - from typing import List, Sequence, Set -5 + pass -6 | -7 | -8 | def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap index 201e5cc19a1ba..6c5ead27428ce 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_TC004_9.py.snap @@ -1,44 +1,4 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `typing.Tuple` out of type-checking block. Import is used for more than type hinting. - --> TC004_9.py:4:24 - | -3 | if TYPE_CHECKING: -4 | from typing import Tuple, List, Dict - | ^^^^^ -5 | -6 | x: Tuple - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import Tuple, List -3 | -4 | if TYPE_CHECKING: - - from typing import Tuple, List, Dict -5 + from typing import Dict -6 | -7 | x: Tuple -8 | -note: This is an unsafe fix and may change runtime behavior -TC004 [*] Move import `typing.List` out of type-checking block. Import is used for more than type hinting. - --> TC004_9.py:4:31 - | -3 | if TYPE_CHECKING: -4 | from typing import Tuple, List, Dict - | ^^^^ -5 | -6 | x: Tuple - | -help: Move out of type-checking block -1 | from typing import TYPE_CHECKING -2 + from typing import Tuple, List -3 | -4 | if TYPE_CHECKING: - - from typing import Tuple, List, Dict -5 + from typing import Dict -6 | -7 | x: Tuple -8 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap index 53e45cce3a59d..22bae5d08e6d2 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-import-in-type-checking-block_quote.py.snap @@ -1,31 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting. - --> quote.py:57:28 - | -56 | if TYPE_CHECKING: -57 | from pandas import DataFrame - | ^^^^^^^^^ -58 | -59 | def func(value: DataFrame): - | -help: Move out of type-checking block -1 + from pandas import DataFrame -2 | def f(): -3 | from pandas import DataFrame -4 | --------------------------------------------------------------------------------- -55 | from typing import TYPE_CHECKING -56 | -57 | if TYPE_CHECKING: - - from pandas import DataFrame -58 + pass -59 | -60 | def func(value: DataFrame): -61 | ... -note: This is an unsafe fix and may change runtime behavior - TC004 [*] Move import `pandas.DataFrame` out of type-checking block. Import is used for more than type hinting. --> quote.py:110:28 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap index ab0b2a8952e63..3588e7b2414fa 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__runtime-string-union_TC010_2.py.snap @@ -1,39 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs --- -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:4:4 - | -4 | x: "int" | str # TC010 - | ^^^^^ -5 | x: ("int" | str) | "bool" # TC010 - | - -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:5:5 - | -4 | x: "int" | str # TC010 -5 | x: ("int" | str) | "bool" # TC010 - | ^^^^^ - | - -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:5:20 - | -4 | x: "int" | str # TC010 -5 | x: ("int" | str) | "bool" # TC010 - | ^^^^^^ - | - -TC010 Invalid string member in `X | Y`-style union type - --> TC010_2.py:12:20 - | -12 | z: list[str, str | "int"] = [] # TC010 - | ^^^^^ -13 | -14 | type A = Value["int" | str] # OK - | - TC010 Invalid string member in `X | Y`-style union type --> TC010_2.py:16:30 | diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap index 2d3f3068076ad..d84c1fff21d2e 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_TC002.py.snap @@ -220,32 +220,3 @@ help: Move into type-checking block 52 | x = dict["pd.DataFrame", "pd.DataFrame"] 53 | note: This is an unsafe fix and may change runtime behavior - -TC002 [*] Move third-party import `module.Member` into a type-checking block - --> TC002.py:172:24 - | -170 | global Member -171 | -172 | from module import Member - | ^^^^^^ -173 | -174 | x: Member = 1 - | -help: Move into type-checking block -1 | """Tests to determine accurate detection of typing-only imports.""" -2 + from typing import TYPE_CHECKING -3 + -4 + if TYPE_CHECKING: -5 + from module import Member -6 | -7 | -8 | def f(): --------------------------------------------------------------------------------- -173 | def f(): -174 | global Member -175 | - - from module import Member -176 | -177 | x: Member = 1 -178 | -note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap index 173cb5a2d6539..8efb644122114 100644 --- a/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap +++ b/crates/ruff_linter/src/rules/flake8_type_checking/snapshots/ruff_linter__rules__flake8_type_checking__tests__typing-only-third-party-import_quote.py.snap @@ -1,5 +1,348 @@ --- source: crates/ruff_linter/src/rules/flake8_type_checking/mod.rs -snapshot_kind: text --- +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:2:24 + | +1 | def f(): +2 | from pandas import DataFrame + | ^^^^^^^^^ +3 | +4 | def baz() -> DataFrame: + | +help: Move into type-checking block + - def f(): +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 | from pandas import DataFrame +5 + def f(): +6 | +7 | def baz() -> DataFrame: +8 | ... +note: This is an unsafe fix and may change runtime behavior +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:9:24 + | + 8 | def f(): + 9 | from pandas import DataFrame + | ^^^^^^^^^ +10 | +11 | def baz() -> DataFrame[int]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +10 | +11 | +12 | def f(): + - from pandas import DataFrame +13 | +14 | def baz() -> DataFrame[int]: +15 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas` into a type-checking block + --> quote.py:16:22 + | +15 | def f(): +16 | import pandas as pd + | ^^ +17 | +18 | def baz() -> pd.DataFrame: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + import pandas as pd +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +17 | +18 | +19 | def f(): + - import pandas as pd +20 | +21 | def baz() -> pd.DataFrame: +22 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas` into a type-checking block + --> quote.py:23:22 + | +22 | def f(): +23 | import pandas as pd + | ^^ +24 | +25 | def baz() -> pd.DataFrame.Extra: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + import pandas as pd +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +24 | +25 | +26 | def f(): + - import pandas as pd +27 | +28 | def baz() -> pd.DataFrame.Extra: +29 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas` into a type-checking block + --> quote.py:30:22 + | +29 | def f(): +30 | import pandas as pd + | ^^ +31 | +32 | def baz() -> pd.DataFrame | int: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + import pandas as pd +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +31 | +32 | +33 | def f(): + - import pandas as pd +34 | +35 | def baz() -> pd.DataFrame | int: +36 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:38:24 + | +37 | def f(): +38 | from pandas import DataFrame + | ^^^^^^^^^ +39 | +40 | def baz() -> DataFrame(): + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +39 | +40 | +41 | def f(): + - from pandas import DataFrame +42 | +43 | def baz() -> DataFrame(): +44 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:47:24 + | +45 | from typing import Literal +46 | +47 | from pandas import DataFrame + | ^^^^^^^^^ +48 | +49 | def baz() -> DataFrame[Literal["int"]]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +48 | def f(): +49 | from typing import Literal +50 | + - from pandas import DataFrame +51 | +52 | def baz() -> DataFrame[Literal["int"]]: +53 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:64:24 + | +63 | def f(): +64 | from pandas import DataFrame, Series + | ^^^^^^^^^ +65 | +66 | def baz() -> DataFrame | Series: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +65 | +66 | +67 | def f(): + - from pandas import DataFrame, Series +68 | +69 | def baz() -> DataFrame | Series: +70 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.Series` into a type-checking block + --> quote.py:64:35 + | +63 | def f(): +64 | from pandas import DataFrame, Series + | ^^^^^^ +65 | +66 | def baz() -> DataFrame | Series: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +65 | +66 | +67 | def f(): + - from pandas import DataFrame, Series +68 | +69 | def baz() -> DataFrame | Series: +70 | ... +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:71:24 + | +70 | def f(): +71 | from pandas import DataFrame, Series + | ^^^^^^^^^ +72 | +73 | def baz() -> ( + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +72 | +73 | +74 | def f(): + - from pandas import DataFrame, Series +75 | +76 | def baz() -> ( +77 | DataFrame | +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.Series` into a type-checking block + --> quote.py:71:35 + | +70 | def f(): +71 | from pandas import DataFrame, Series + | ^^^^^^ +72 | +73 | def baz() -> ( + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +72 | +73 | +74 | def f(): + - from pandas import DataFrame, Series +75 | +76 | def baz() -> ( +77 | DataFrame | +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.DataFrame` into a type-checking block + --> quote.py:89:24 + | +88 | def f(): +89 | from pandas import DataFrame, Series + | ^^^^^^^^^ +90 | +91 | def func(self) -> DataFrame | list[Series]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +90 | +91 | +92 | def f(): + - from pandas import DataFrame, Series +93 | +94 | def func(self) -> DataFrame | list[Series]: +95 | pass +note: This is an unsafe fix and may change runtime behavior + +TC002 [*] Move third-party import `pandas.Series` into a type-checking block + --> quote.py:89:35 + | +88 | def f(): +89 | from pandas import DataFrame, Series + | ^^^^^^ +90 | +91 | def func(self) -> DataFrame | list[Series]: + | +help: Move into type-checking block +1 + from typing import TYPE_CHECKING +2 + +3 + if TYPE_CHECKING: +4 + from pandas import DataFrame, Series +5 | def f(): +6 | from pandas import DataFrame +7 | +-------------------------------------------------------------------------------- +90 | +91 | +92 | def f(): + - from pandas import DataFrame, Series +93 | +94 | def func(self) -> DataFrame | list[Series]: +95 | pass +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs b/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs index f7f6c64a8d657..5d0938d5c65b3 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs @@ -11,11 +11,11 @@ mod tests { use ruff_python_ast::PythonVersion; use test_case::test_case; - use crate::assert_diagnostics; use crate::registry::Rule; use crate::settings; use crate::settings::types::PreviewMode; use crate::test::test_path; + use crate::{assert_diagnostics, assert_diagnostics_diff}; #[test_case(Path::new("full_name.py"))] #[test_case(Path::new("import_as.py"))] @@ -82,6 +82,29 @@ mod tests { Ok(()) } + #[test_case(Rule::InvalidPathlibWithSuffix, Path::new("PTH210.py"))] + #[test_case(Rule::InvalidPathlibWithSuffix, Path::new("PTH210_1.py"))] + fn deferred_annotations_diff(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "deferred_annotations_diff_{}_{}", + rule_code.name(), + path.to_string_lossy() + ); + assert_diagnostics_diff!( + snapshot, + Path::new("flake8_use_pathlib").join(path).as_path(), + &settings::LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + ..settings::LinterSettings::for_rule(rule_code) + }, + &settings::LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + ..settings::LinterSettings::for_rule(rule_code) + }, + ); + Ok(()) + } + #[test_case(Path::new("full_name.py"))] #[test_case(Path::new("import_as.py"))] #[test_case(Path::new("import_from_as.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap index d2a76a210ee3c..9a057749d1b79 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210.py.snap @@ -1,17 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs --- -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:21:1 - | -20 | ### Errors -21 | path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^ -22 | path.with_suffix("py") -23 | path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:22:1 | @@ -95,18 +84,6 @@ help: Add a leading dot 28 | posix_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:27:1 - | -25 | path.with_suffix(suffix="js") -26 | -27 | posix_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -28 | posix_path.with_suffix("py") -29 | posix_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:28:1 | @@ -189,18 +166,6 @@ help: Add a leading dot 34 | pure_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:33:1 - | -31 | posix_path.with_suffix(suffix="js") -32 | -33 | pure_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -34 | pure_path.with_suffix("py") -35 | pure_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:34:1 | @@ -283,18 +248,6 @@ help: Add a leading dot 40 | pure_posix_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:39:1 - | -37 | pure_path.with_suffix(suffix="js") -38 | -39 | pure_posix_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -40 | pure_posix_path.with_suffix("py") -41 | pure_posix_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:40:1 | @@ -377,18 +330,6 @@ help: Add a leading dot 46 | pure_windows_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:45:1 - | -43 | pure_posix_path.with_suffix(suffix="js") -44 | -45 | pure_windows_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -46 | pure_windows_path.with_suffix("py") -47 | pure_windows_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:46:1 | @@ -471,18 +412,6 @@ help: Add a leading dot 52 | windows_path.with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:51:1 - | -49 | pure_windows_path.with_suffix(suffix="js") -50 | -51 | windows_path.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -52 | windows_path.with_suffix("py") -53 | windows_path.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:52:1 | @@ -565,18 +494,6 @@ help: Add a leading dot 58 | Path().with_suffix("py") note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210.py:57:1 - | -55 | windows_path.with_suffix(suffix="js") -56 | -57 | Path().with_suffix(".") - | ^^^^^^^^^^^^^^^^^^^^^^^ -58 | Path().with_suffix("py") -59 | PosixPath().with_suffix("py") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210.py:58:1 | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap index adb00cbf99df8..1f523ea194dd4 100644 --- a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__PTH210_PTH210_1.py.snap @@ -1,18 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs --- -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:13:5 - | -11 | def test_path(p: Path) -> None: -12 | ## Errors -13 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -14 | p.with_suffix("py") -15 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:14:5 | @@ -96,18 +84,6 @@ help: Add a leading dot 20 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:31:5 - | -29 | def test_posix_path(p: PosixPath) -> None: -30 | ## Errors -31 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -32 | p.with_suffix("py") -33 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:32:5 | @@ -191,18 +167,6 @@ help: Add a leading dot 38 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:49:5 - | -47 | def test_pure_path(p: PurePath) -> None: -48 | ## Errors -49 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -50 | p.with_suffix("py") -51 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:50:5 | @@ -286,18 +250,6 @@ help: Add a leading dot 56 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:67:5 - | -65 | def test_pure_posix_path(p: PurePosixPath) -> None: -66 | ## Errors -67 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -68 | p.with_suffix("py") -69 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:68:5 | @@ -381,18 +333,6 @@ help: Add a leading dot 74 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:85:5 - | -83 | def test_pure_windows_path(p: PureWindowsPath) -> None: -84 | ## Errors -85 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -86 | p.with_suffix("py") -87 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:86:5 | @@ -476,18 +416,6 @@ help: Add a leading dot 92 | p.with_suffix() note: This is an unsafe fix and may change runtime behavior -PTH210 Invalid suffix passed to `.with_suffix()` - --> PTH210_1.py:103:5 - | -101 | def test_windows_path(p: WindowsPath) -> None: -102 | ## Errors -103 | p.with_suffix(".") - | ^^^^^^^^^^^^^^^^^^ -104 | p.with_suffix("py") -105 | p.with_suffix(r"s") - | -help: Remove "." or extend to valid suffix - PTH210 [*] Dotless suffix passed to `.with_suffix()` --> PTH210_1.py:104:5 | diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210.py.snap new file mode 100644 index 0000000000000..3a9523b277319 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210.py.snap @@ -0,0 +1,100 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 7 +Added: 0 + +--- Removed --- +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:21:1 + | +20 | ### Errors +21 | path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^ +22 | path.with_suffix("py") +23 | path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:27:1 + | +25 | path.with_suffix(suffix="js") +26 | +27 | posix_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +28 | posix_path.with_suffix("py") +29 | posix_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:33:1 + | +31 | posix_path.with_suffix(suffix="js") +32 | +33 | pure_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +34 | pure_path.with_suffix("py") +35 | pure_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:39:1 + | +37 | pure_path.with_suffix(suffix="js") +38 | +39 | pure_posix_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +40 | pure_posix_path.with_suffix("py") +41 | pure_posix_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:45:1 + | +43 | pure_posix_path.with_suffix(suffix="js") +44 | +45 | pure_windows_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +46 | pure_windows_path.with_suffix("py") +47 | pure_windows_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:51:1 + | +49 | pure_windows_path.with_suffix(suffix="js") +50 | +51 | windows_path.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +52 | windows_path.with_suffix("py") +53 | windows_path.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210.py:57:1 + | +55 | windows_path.with_suffix(suffix="js") +56 | +57 | Path().with_suffix(".") + | ^^^^^^^^^^^^^^^^^^^^^^^ +58 | Path().with_suffix("py") +59 | PosixPath().with_suffix("py") + | +help: Remove "." or extend to valid suffix diff --git a/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210_1.py.snap b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210_1.py.snap new file mode 100644 index 0000000000000..ee2bdd198824a --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_use_pathlib/snapshots/ruff_linter__rules__flake8_use_pathlib__tests__deferred_annotations_diff_invalid-pathlib-with-suffix_PTH210_1.py.snap @@ -0,0 +1,88 @@ +--- +source: crates/ruff_linter/src/rules/flake8_use_pathlib/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 6 +Added: 0 + +--- Removed --- +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:13:5 + | +11 | def test_path(p: Path) -> None: +12 | ## Errors +13 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +14 | p.with_suffix("py") +15 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:31:5 + | +29 | def test_posix_path(p: PosixPath) -> None: +30 | ## Errors +31 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +32 | p.with_suffix("py") +33 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:49:5 + | +47 | def test_pure_path(p: PurePath) -> None: +48 | ## Errors +49 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +50 | p.with_suffix("py") +51 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:67:5 + | +65 | def test_pure_posix_path(p: PurePosixPath) -> None: +66 | ## Errors +67 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +68 | p.with_suffix("py") +69 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:85:5 + | +83 | def test_pure_windows_path(p: PureWindowsPath) -> None: +84 | ## Errors +85 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +86 | p.with_suffix("py") +87 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix + + +PTH210 Invalid suffix passed to `.with_suffix()` + --> PTH210_1.py:103:5 + | +101 | def test_windows_path(p: WindowsPath) -> None: +102 | ## Errors +103 | p.with_suffix(".") + | ^^^^^^^^^^^^^^^^^^ +104 | p.with_suffix("py") +105 | p.with_suffix(r"s") + | +help: Remove "." or extend to valid suffix diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index 6f3e7bbafe22f..3dd7edde6555b 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -3769,7 +3769,7 @@ lambda: fu def f(a: A) -> A: pass class A: pass ", - &[Rule::UndefinedName, Rule::UndefinedName], + &[], ); flakes( r" @@ -3783,7 +3783,7 @@ lambda: fu a: A class A: pass ", - &[Rule::UndefinedName], + &[], ); flakes( r" diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap index a487e4ddb80cf..95d305eb2b0f3 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_18.py.snap @@ -1,5 +1,11 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs -snapshot_kind: text --- - +F821 Undefined name `y` + --> F821_18.py:19:7 + | +17 | # This is no longer allowed on Python 3.14+ +18 | x: (y := 1) +19 | print(y) + | ^ + | diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap index 89dd2df81fa10..d0b409f39ee0b 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_2.py.snap @@ -1,12 +1,4 @@ --- source: crates/ruff_linter/src/rules/pyflakes/mod.rs --- -F821 Undefined name `Model` - --> F821_2.py:5:13 - | -4 | # F821 Undefined name `Model` -5 | x: Literal["Model"] - | ^^^^^ -6 | -7 | from typing_extensions import Literal - | + diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap index 9bb838ffff566..121eb3edd0044 100644 --- a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_26.py.snap @@ -21,48 +21,6 @@ F821 Undefined name `C` 12 | CStr2: TypeAlias = Union["C", str] # always okay | -F821 Undefined name `C` - --> F821_26.py:16:12 - | -14 | # References to a class from inside the class: -15 | class C: -16 | other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -17 | other2: "C" = ... # always okay -18 | def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file - | - -F821 Undefined name `C` - --> F821_26.py:18:35 - | -16 | other: C = ... # valid in a `.pyi` stub file, not in a `.py` runtime file -17 | other2: "C" = ... # always okay -18 | def from_str(self, s: str) -> C: ... # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -19 | def from_str2(self, s: str) -> "C": ... # always okay - | - -F821 Undefined name `B` - --> F821_26.py:23:10 - | -21 | # Circular references: -22 | class A: -23 | foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -24 | foo2: "B" # always okay -25 | bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file - | - -F821 Undefined name `B` - --> F821_26.py:25:20 - | -23 | foo: B # valid in a `.pyi` stub file, not in a `.py` runtime file -24 | foo2: "B" # always okay -25 | bar: dict[str, B] # valid in a `.pyi` stub file, not in a `.py` runtime file - | ^ -26 | bar2: dict[str, "A"] # always okay - | - F821 Undefined name `Tree` --> F821_26.py:33:17 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap index e03ab73fc85c2..272d7d64e796e 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP006_2.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pyupgrade/mod.rs --- -UP006 Use `collections.defaultdict` instead of `typing.DefaultDict` for type annotation +UP006 [*] Use `collections.defaultdict` instead of `typing.DefaultDict` for type annotation --> UP006_2.py:7:10 | 7 | def f(x: typing.DefaultDict[str, str]) -> None: @@ -9,3 +9,9 @@ UP006 Use `collections.defaultdict` instead of `typing.DefaultDict` for type ann 8 | ... | help: Replace with `collections.defaultdict` +4 | from collections import defaultdict +5 | +6 | + - def f(x: typing.DefaultDict[str, str]) -> None: +7 + def f(x: defaultdict[str, str]) -> None: +8 | ... diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap index c050ba4988456..d2ce0fd60442c 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP007.py.snap @@ -171,6 +171,40 @@ help: Convert to `X | Y` 35 | 36 | +UP007 [*] Use `X | Y` for type annotations + --> UP007.py:37:10 + | +37 | def f(x: Union["str", int]) -> None: + | ^^^^^^^^^^^^^^^^^ +38 | ... + | +help: Convert to `X | Y` +34 | ... +35 | +36 | + - def f(x: Union["str", int]) -> None: +37 + def f(x: "str" | int) -> None: +38 | ... +39 | +40 | + +UP007 [*] Use `X | Y` for type annotations + --> UP007.py:41:10 + | +41 | def f(x: Union[("str", "int"), float]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +42 | ... + | +help: Convert to `X | Y` +38 | ... +39 | +40 | + - def f(x: Union[("str", "int"), float]) -> None: +41 + def f(x: "str" | "int" | float) -> None: +42 | ... +43 | +44 | + UP007 Use `X | Y` for type annotations --> UP007.py:46:9 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap index 82253977ca081..a1983376481f3 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_0.py.snap @@ -685,6 +685,46 @@ help: Remove outdated version block 184 | print("py3") note: This is an unsafe fix and may change runtime behavior +UP036 [*] Version block is outdated for minimum Python version + --> UP036_0.py:185:4 + | +183 | print("py3") +184 | +185 | if sys.version_info <= (3,13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +186 | print("py3") + | +help: Remove outdated version block +182 | if sys.version_info < (3,13): +183 | print("py3") +184 | + - if sys.version_info <= (3,13): + - print("py3") +185 | +186 | if sys.version_info <= (3,13): +187 | print("py3") +note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_0.py:188:4 + | +186 | print("py3") +187 | +188 | if sys.version_info <= (3,13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +189 | print("py3") + | +help: Remove outdated version block +185 | if sys.version_info <= (3,13): +186 | print("py3") +187 | + - if sys.version_info <= (3,13): + - print("py3") +188 | +189 | if sys.version_info == 10000000: +190 | print("py3") +note: This is an unsafe fix and may change runtime behavior + UP036 Version specifier is invalid --> UP036_0.py:191:24 | @@ -715,6 +755,27 @@ UP036 Version specifier is invalid 198 | print("py3") | +UP036 [*] Version block is outdated for minimum Python version + --> UP036_0.py:200:4 + | +198 | print("py3") +199 | +200 | if sys.version_info > (3,13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +201 | print("py3") + | +help: Remove outdated version block +197 | if sys.version_info <= (3,10000000): +198 | print("py3") +199 | + - if sys.version_info > (3,13): + - print("py3") +200 + print("py3") +201 | +202 | if sys.version_info >= (3,13): +203 | print("py3") +note: This is an unsafe fix and may change runtime behavior + UP036 [*] Version block is outdated for minimum Python version --> UP036_0.py:203:4 | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap index ea5a1b487a573..98e269b932725 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP036_5.py.snap @@ -159,6 +159,66 @@ UP036 Version specifier is invalid 49 | print() | +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:60:4 + | +58 | print() +59 | +60 | if sys.version_info <= (3, 13, 0): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +61 | print() + | +help: Remove outdated version block +57 | if sys.version_info <= (3, 13, 'final'): +58 | print() +59 | + - if sys.version_info <= (3, 13, 0): + - print() +60 | +61 | if sys.version_info < (3, 13, 37): +62 | print() +note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:63:4 + | +61 | print() +62 | +63 | if sys.version_info < (3, 13, 37): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +64 | print() + | +help: Remove outdated version block +60 | if sys.version_info <= (3, 13, 0): +61 | print() +62 | + - if sys.version_info < (3, 13, 37): + - print() +63 | +64 | if sys.version_info <= (3, 13, 37): +65 | print() +note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:66:4 + | +64 | print() +65 | +66 | if sys.version_info <= (3, 13, 37): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +67 | print() + | +help: Remove outdated version block +63 | if sys.version_info < (3, 13, 37): +64 | print() +65 | + - if sys.version_info <= (3, 13, 37): + - print() +66 | +67 | if sys.version_info <= (3, 14, 0): +68 | print() +note: This is an unsafe fix and may change runtime behavior + UP036 [*] Version block is outdated for minimum Python version --> UP036_5.py:77:4 | @@ -278,3 +338,27 @@ help: Remove outdated version block 99 | # Semantically incorrect, skip fixing 100 | note: This is an unsafe fix and may change runtime behavior + +UP036 [*] Version block is outdated for minimum Python version + --> UP036_5.py:109:4 + | +107 | print(2) +108 | +109 | if sys.version_info.major > (3, 13): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +110 | print(3) +111 | else: + | +help: Remove outdated version block +106 | else: +107 | print(2) +108 | + - if sys.version_info.major > (3, 13): + - print(3) + - else: + - print(2) +109 + print(3) +110 | +111 | if sys.version_info.major[:2] > (3, 13): +112 | print(3) +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap index 26631a2904a48..04fbed2e63a29 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP037_1.py.snap @@ -18,4 +18,18 @@ help: Remove quotes 9 + x: Tuple[int, int] = (0, 0) 10 | print(x) 11 | -12 | +12 | + +UP037 [*] Remove quotes from type annotation + --> UP037_1.py:14:4 + | +13 | # OK +14 | X: "Tuple[int, int]" = (0, 0) + | ^^^^^^^^^^^^^^^^^ + | +help: Remove quotes +11 | +12 | +13 | # OK + - X: "Tuple[int, int]" = (0, 0) +14 + X: Tuple[int, int] = (0, 0) diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap index fa2af22e0cdc8..04fbed2e63a29 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__add_future_annotation_UP037_1.py.snap @@ -28,14 +28,8 @@ UP037 [*] Remove quotes from type annotation | ^^^^^^^^^^^^^^^^^ | help: Remove quotes -1 + from __future__ import annotations -2 | from typing import TYPE_CHECKING -3 | -4 | if TYPE_CHECKING: --------------------------------------------------------------------------------- +11 | 12 | -13 | -14 | # OK +13 | # OK - X: "Tuple[int, int]" = (0, 0) -15 + X: Tuple[int, int] = (0, 0) -note: This is an unsafe fix and may change runtime behavior +14 + X: Tuple[int, int] = (0, 0) diff --git a/crates/ruff_linter/src/rules/ruff/mod.rs b/crates/ruff_linter/src/rules/ruff/mod.rs index b484095aae8ea..7cdc55784176e 100644 --- a/crates/ruff_linter/src/rules/ruff/mod.rs +++ b/crates/ruff_linter/src/rules/ruff/mod.rs @@ -22,7 +22,7 @@ mod tests { use crate::settings::LinterSettings; use crate::settings::types::{CompiledPerFileIgnoreList, PerFileIgnore, PreviewMode}; use crate::test::{test_path, test_resource_path}; - use crate::{assert_diagnostics, settings}; + use crate::{assert_diagnostics, assert_diagnostics_diff, settings}; #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005.py"))] #[test_case(Rule::CollectionLiteralConcatenation, Path::new("RUF005_slices.py"))] @@ -245,6 +245,32 @@ mod tests { Ok(()) } + #[test] + fn confusables_deferred_annotations_diff() -> Result<()> { + assert_diagnostics_diff!( + Path::new("ruff/confusables.py"), + &LinterSettings { + unresolved_target_version: PythonVersion::PY313.into(), + allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']), + ..settings::LinterSettings::for_rules(vec![ + Rule::AmbiguousUnicodeCharacterString, + Rule::AmbiguousUnicodeCharacterDocstring, + Rule::AmbiguousUnicodeCharacterComment, + ]) + }, + &LinterSettings { + unresolved_target_version: PythonVersion::PY314.into(), + allowed_confusables: FxHashSet::from_iter(['−', 'ρ', '∗']), + ..settings::LinterSettings::for_rules(vec![ + Rule::AmbiguousUnicodeCharacterString, + Rule::AmbiguousUnicodeCharacterDocstring, + Rule::AmbiguousUnicodeCharacterComment, + ]) + }, + ); + Ok(()) + } + #[test] fn preview_confusables() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap index 83bc6b8d0c187..62756e9e9e7a1 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF058_RUF058_0.py.snap @@ -215,4 +215,62 @@ help: Use `map` instead 59 + 60 | ) 61 | -62 | +62 | + +RUF058 [*] `itertools.starmap` called on `zip` iterable + --> RUF058_0.py:71:1 + | +69 | starmap(func, zip(a, b, c), lorem=ipsum) +70 | +71 | starmap(func, zip(a, b, c, strict=True)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +72 | starmap(func, zip(a, b, c, strict=False)) +73 | starmap(func, zip(a, b, c, strict=strict)) + | +help: Use `map` instead +68 | starmap(func, zip(a, b, c, lorem=ipsum)) +69 | starmap(func, zip(a, b, c), lorem=ipsum) +70 | + - starmap(func, zip(a, b, c, strict=True)) +71 + map(func, a, b, c, strict=True) +72 | starmap(func, zip(a, b, c, strict=False)) +73 | starmap(func, zip(a, b, c, strict=strict)) +74 | + +RUF058 [*] `itertools.starmap` called on `zip` iterable + --> RUF058_0.py:72:1 + | +71 | starmap(func, zip(a, b, c, strict=True)) +72 | starmap(func, zip(a, b, c, strict=False)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +73 | starmap(func, zip(a, b, c, strict=strict)) + | +help: Use `map` instead +69 | starmap(func, zip(a, b, c), lorem=ipsum) +70 | +71 | starmap(func, zip(a, b, c, strict=True)) + - starmap(func, zip(a, b, c, strict=False)) +72 + map(func, a, b, c, strict=False) +73 | starmap(func, zip(a, b, c, strict=strict)) +74 | +75 | # https://github.com/astral-sh/ruff/issues/15742 + +RUF058 [*] `itertools.starmap` called on `zip` iterable + --> RUF058_0.py:73:1 + | +71 | starmap(func, zip(a, b, c, strict=True)) +72 | starmap(func, zip(a, b, c, strict=False)) +73 | starmap(func, zip(a, b, c, strict=strict)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +74 | +75 | # https://github.com/astral-sh/ruff/issues/15742 + | +help: Use `map` instead +70 | +71 | starmap(func, zip(a, b, c, strict=True)) +72 | starmap(func, zip(a, b, c, strict=False)) + - starmap(func, zip(a, b, c, strict=strict)) +73 + map(func, a, b, c, strict=strict) +74 | +75 | # https://github.com/astral-sh/ruff/issues/15742 +76 | starmap(func, zip(*a)) diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap index 4e9ab5604cd15..27e8db62096c1 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables.snap @@ -191,11 +191,3 @@ RUF001 String contains ambiguous `𝐁` (MATHEMATICAL BOLD CAPITAL B). Did you m 59 | 60 | from typing import Literal | - -RUF001 String contains ambiguous `ﮨ` (ARABIC LETTER HEH GOAL INITIAL FORM). Did you mean `o` (LATIN SMALL LETTER O)? - --> confusables.py:61:20 - | -60 | from typing import Literal -61 | x: '''"""'Literal["ﮨ"]'"""''' - | ^ - | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap new file mode 100644 index 0000000000000..3411053a0d20f --- /dev/null +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap @@ -0,0 +1,19 @@ +--- +source: crates/ruff_linter/src/rules/ruff/mod.rs +--- +--- Linter settings --- +-linter.unresolved_target_version = 3.13 ++linter.unresolved_target_version = 3.14 + +--- Summary --- +Removed: 1 +Added: 0 + +--- Removed --- +RUF001 String contains ambiguous `ﮨ` (ARABIC LETTER HEH GOAL INITIAL FORM). Did you mean `o` (LATIN SMALL LETTER O)? + --> confusables.py:61:20 + | +60 | from typing import Literal +61 | x: '''"""'Literal["ﮨ"]'"""''' + | ^ + | diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap index d9c235de30d80..332cf21fec8b4 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview_confusables.snap @@ -200,11 +200,3 @@ RUF001 String contains ambiguous `𝐁` (MATHEMATICAL BOLD CAPITAL B). Did you m 59 | 60 | from typing import Literal | - -RUF001 String contains ambiguous `ﮨ` (ARABIC LETTER HEH GOAL INITIAL FORM). Did you mean `o` (LATIN SMALL LETTER O)? - --> confusables.py:61:20 - | -60 | from typing import Literal -61 | x: '''"""'Literal["ﮨ"]'"""''' - | ^ - | diff --git a/crates/ruff_linter/src/test.rs b/crates/ruff_linter/src/test.rs index 2ebb244d62f66..01eb02705e370 100644 --- a/crates/ruff_linter/src/test.rs +++ b/crates/ruff_linter/src/test.rs @@ -508,12 +508,18 @@ macro_rules! assert_diagnostics { #[macro_export] macro_rules! assert_diagnostics_diff { - ($snapshot:expr, $path:expr, $settings_before:expr, $settings_after:expr) => {{ + ($snapshot:expr, $path:expr, $settings_before:expr, $settings_after:expr $(,)?) => {{ let diff = $crate::test::test_path_with_settings_diff($path, $settings_before, $settings_after)?; insta::with_settings!({ omit_expression => true }, { insta::assert_snapshot!($snapshot, format!("{}", diff)); }); }}; + ($path:expr, $settings_before:expr, $settings_after:expr $(,)?) => {{ + let diff = $crate::test::test_path_with_settings_diff($path, $settings_before, $settings_after)?; + insta::with_settings!({ omit_expression => true }, { + insta::assert_snapshot!(format!("{}", diff)); + }); + }}; } #[cfg(test)] diff --git a/crates/ruff_python_ast/src/python_version.rs b/crates/ruff_python_ast/src/python_version.rs index 7eab154ad1317..2cedb435fd592 100644 --- a/crates/ruff_python_ast/src/python_version.rs +++ b/crates/ruff_python_ast/src/python_version.rs @@ -55,9 +55,8 @@ impl PythonVersion { Self::PY37 } - // TODO: change this to 314 when it is released pub const fn latest() -> Self { - Self::PY313 + Self::PY314 } /// The latest Python version supported in preview @@ -94,7 +93,7 @@ impl PythonVersion { impl Default for PythonVersion { fn default() -> Self { - Self::PY39 + Self::PY310 } } diff --git a/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap index 58cfb7e492610..60177086ceebd 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@blank_line_before_class_docstring.py.snap @@ -56,7 +56,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap index 9ca83e81deeb2..e3117a2e391c0 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring.py.snap @@ -175,7 +175,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -351,7 +351,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -527,7 +527,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -703,7 +703,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -879,7 +879,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap index 024d662e0ae6b..26fcfcff6c0ae 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples.py.snap @@ -1368,7 +1368,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -2740,7 +2740,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -4112,7 +4112,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -5484,7 +5484,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -6856,7 +6856,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -8221,7 +8221,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -9586,7 +9586,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -10960,7 +10960,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -12325,7 +12325,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = 60 preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -13699,7 +13699,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap index 9b1dc4ba525f8..c40ab98413299 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_crlf.py.snap @@ -27,7 +27,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap index 7112865500923..628910f153348 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_code_examples_dynamic_line_width.py.snap @@ -308,7 +308,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -879,7 +879,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -1425,7 +1425,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -1996,7 +1996,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap index a7e5f5d84fc47..c6736bcfa9644 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@docstring_tab_indentation.py.snap @@ -90,7 +90,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -184,7 +184,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap index 0e9e65cf924c1..7b021391c9319 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__bytes.py.snap @@ -140,7 +140,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -296,7 +296,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap index c929f160dff59..40ac13cb244ec 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring.py.snap @@ -768,7 +768,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -1595,7 +1595,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap index 508eda3e94cb7..052758abce3bf 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__fstring_preview.py.snap @@ -38,7 +38,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap index a9d9f10827ea8..526d24e02a00c 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__join_implicit_concatenated_string_preserve.py.snap @@ -40,7 +40,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap index 953b2c34cbc8b..0da0050e34f42 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@expression__string.py.snap @@ -232,7 +232,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -482,7 +482,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap index c7775aba83819..db1d53e68b7b5 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__fmt_off_docstring.py.snap @@ -37,7 +37,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -75,7 +75,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap index 8fd14d663462f..3a68d9ceae4e6 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__indent.py.snap @@ -18,7 +18,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -37,7 +37,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -56,7 +56,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap index 99bde1d1d63ec..1c52065c5a469 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@fmt_on_off__mixed_space_and_tab.py.snap @@ -33,7 +33,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -68,7 +68,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -103,7 +103,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap index 6018c9fc26a27..6195b43c2185b 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@notebook_docstring.py.snap @@ -24,7 +24,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Ipynb ``` @@ -48,7 +48,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap index 3557b8c4781b5..f268f65783b37 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@preview.py.snap @@ -84,7 +84,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -167,7 +167,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap index b79b0ead6e0e9..a430dc1bb7672 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@quote_style.py.snap @@ -68,7 +68,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -142,7 +142,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -216,7 +216,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap index 0cb72a9fb45f8..cad5ee4f2ae58 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__docstring_code_examples.py.snap @@ -121,7 +121,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -273,7 +273,7 @@ magic-trailing-comma = Respect docstring-code = Enabled docstring-code-line-width = 88 preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap index 669f8f5eb52ac..1609cf657e9d7 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__indent.py.snap @@ -81,7 +81,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -160,7 +160,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -239,7 +239,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap index d2ec8f05d87f5..5610ef79ee1a3 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@range_formatting__stub.pyi.snap @@ -34,7 +34,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap index 12521c94333fb..e427d077c72fb 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@skip_magic_trailing_comma.py.snap @@ -51,7 +51,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -109,7 +109,7 @@ magic-trailing-comma = Ignore docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap index 1d7b8c76363ca..a1efd92a9c8a6 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class.pyi.snap @@ -201,7 +201,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap index 55485257f392e..9e9dc166fbe73 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@stub_files__blank_line_after_nested_stub_class_eof.pyi.snap @@ -35,7 +35,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Enabled -target_version = 3.9 +target_version = 3.10 source_type = Stub ``` diff --git a/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap index ab1caa6805825..51b77d3f16200 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@tab_width.py.snap @@ -26,7 +26,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -53,7 +53,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` @@ -83,7 +83,7 @@ magic-trailing-comma = Respect docstring-code = Disabled docstring-code-line-width = "dynamic" preview = Disabled -target_version = 3.9 +target_version = 3.10 source_type = Python ``` diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index 055291b53c3be..21fc2269ebacf 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -299,8 +299,8 @@ pub struct Options { /// code upgrades, like rewriting type annotations. Ruff will not propose /// changes using features that are not available in the given version. /// - /// For example, to represent supporting Python >=3.10 or ==3.10 - /// specify `target-version = "py310"`. + /// For example, to represent supporting Python >=3.11 or ==3.11 + /// specify `target-version = "py311"`. /// /// If you're already using a `pyproject.toml` file, we recommend /// `project.requires-python` instead, as it's based on Python packaging @@ -327,8 +327,8 @@ pub struct Options { /// file than it would for an equivalent runtime file with the same target /// version. #[option( - default = r#""py39""#, - value_type = r#""py37" | "py38" | "py39" | "py310" | "py311" | "py312" | "py313""#, + default = r#""py310""#, + value_type = r#""py37" | "py38" | "py39" | "py310" | "py311" | "py312" | "py313" | "py314""#, example = r#" # Always generate Python 3.7-compatible code. target-version = "py37" diff --git a/crates/ty/docs/cli.md b/crates/ty/docs/cli.md index 83de0b3d6dbb1..f9f5580f2b408 100644 --- a/crates/ty/docs/cli.md +++ b/crates/ty/docs/cli.md @@ -86,6 +86,7 @@ over all configuration files.

  • 3.11
  • 3.12
  • 3.13
  • +
  • 3.14
  • --quiet, -q

    Use quiet output (or -qq for silent output)

    --respect-ignore-files

    Respect file exclusions via .gitignore and other standard ignore files. Use --no-respect-gitignore to disable

    --typeshed, --custom-typeshed-dir path

    Custom directory to use for stdlib typeshed stubs

    diff --git a/crates/ty/src/python_version.rs b/crates/ty/src/python_version.rs index 09ce15f526b1a..694e81b55e9d2 100644 --- a/crates/ty/src/python_version.rs +++ b/crates/ty/src/python_version.rs @@ -7,9 +7,9 @@ pub enum PythonVersion { Py37, #[value(name = "3.8")] Py38, - #[default] #[value(name = "3.9")] Py39, + #[default] #[value(name = "3.10")] Py310, #[value(name = "3.11")] @@ -18,6 +18,8 @@ pub enum PythonVersion { Py312, #[value(name = "3.13")] Py313, + #[value(name = "3.14")] + Py314, } impl PythonVersion { @@ -30,6 +32,7 @@ impl PythonVersion { Self::Py311 => "3.11", Self::Py312 => "3.12", Self::Py313 => "3.13", + Self::Py314 => "3.14", } } } @@ -50,6 +53,7 @@ impl From for ruff_python_ast::PythonVersion { PythonVersion::Py311 => Self::PY311, PythonVersion::Py312 => Self::PY312, PythonVersion::Py313 => Self::PY313, + PythonVersion::Py314 => Self::PY314, } } } diff --git a/crates/ty_python_semantic/resources/mdtest/attributes.md b/crates/ty_python_semantic/resources/mdtest/attributes.md index 8f5742bcc7348..1c0c2da772ea7 100644 --- a/crates/ty_python_semantic/resources/mdtest/attributes.md +++ b/crates/ty_python_semantic/resources/mdtest/attributes.md @@ -2109,7 +2109,7 @@ All attribute access on literal `bytes` types is currently delegated to `builtin ```py # revealed: bound method Literal[b"foo"].join(iterable_of_bytes: Iterable[@Todo(Support for `typing.TypeAlias`)], /) -> bytes reveal_type(b"foo".join) -# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`) | tuple[@Todo(Support for `typing.TypeAlias`), ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool +# revealed: bound method Literal[b"foo"].endswith(suffix: @Todo(Support for `typing.TypeAlias`) | tuple[@Todo(Support for `typing.TypeAlias`), ...], start: SupportsIndex | None = EllipsisType, end: SupportsIndex | None = EllipsisType, /) -> bool reveal_type(b"foo".endswith) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/binary/classes.md b/crates/ty_python_semantic/resources/mdtest/binary/classes.md index 89dceef0e28b1..d0ded68b3d6d3 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/classes.md @@ -18,6 +18,11 @@ reveal_type(A | B) # revealed: UnionType ## Union of two classes (prior to 3.10) +```toml +[environment] +python-version = "3.9" +``` + ```py class A: ... class B: ... diff --git a/crates/ty_python_semantic/resources/mdtest/call/getattr_static.md b/crates/ty_python_semantic/resources/mdtest/call/getattr_static.md index a68bae12d2616..a8d87bbfa6155 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/getattr_static.md +++ b/crates/ty_python_semantic/resources/mdtest/call/getattr_static.md @@ -57,7 +57,7 @@ We can access attributes on objects of all kinds: import sys reveal_type(inspect.getattr_static(sys, "dont_write_bytecode")) # revealed: bool -# revealed: def getattr_static(obj: object, attr: str, default: Any | None = ellipsis) -> Any +# revealed: def getattr_static(obj: object, attr: str, default: Any | None = EllipsisType) -> Any reveal_type(inspect.getattr_static(inspect, "getattr_static")) reveal_type(inspect.getattr_static(1, "real")) # revealed: property diff --git a/crates/ty_python_semantic/resources/mdtest/call/methods.md b/crates/ty_python_semantic/resources/mdtest/call/methods.md index f7bc04fba91b8..7db0259951263 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/methods.md +++ b/crates/ty_python_semantic/resources/mdtest/call/methods.md @@ -651,7 +651,7 @@ static_assert(is_assignable_to(TypeOf[property.__set__], Callable)) reveal_type(MyClass.my_property.__set__) static_assert(is_assignable_to(TypeOf[MyClass.my_property.__set__], Callable)) -# revealed: def startswith(self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool +# revealed: def startswith(self, prefix: str | tuple[str, ...], start: SupportsIndex | None = EllipsisType, end: SupportsIndex | None = EllipsisType, /) -> bool reveal_type(str.startswith) static_assert(is_assignable_to(TypeOf[str.startswith], Callable)) @@ -707,7 +707,7 @@ def _( # revealed: (instance: object, value: object, /) -> Unknown reveal_type(j) - # revealed: (self, prefix: str | tuple[str, ...], start: SupportsIndex | None = ellipsis, end: SupportsIndex | None = ellipsis, /) -> bool + # revealed: (self, prefix: str | tuple[str, ...], start: SupportsIndex | None = EllipsisType, end: SupportsIndex | None = EllipsisType, /) -> bool reveal_type(k) # revealed: (prefix: str | tuple[str, ...], start: SupportsIndex | None = None, end: SupportsIndex | None = None, /) -> bool diff --git a/crates/ty_python_semantic/resources/mdtest/scopes/moduletype_attrs.md b/crates/ty_python_semantic/resources/mdtest/scopes/moduletype_attrs.md index 16240e81d966a..b9d92d7fede3f 100644 --- a/crates/ty_python_semantic/resources/mdtest/scopes/moduletype_attrs.md +++ b/crates/ty_python_semantic/resources/mdtest/scopes/moduletype_attrs.md @@ -97,7 +97,7 @@ inside the module: import typing reveal_type(typing.__name__) # revealed: str -reveal_type(typing.__init__) # revealed: bound method ModuleType.__init__(name: str, doc: str | None = ellipsis) -> None +reveal_type(typing.__init__) # revealed: bound method ModuleType.__init__(name: str, doc: str | None = EllipsisType) -> None # For a stub module, we don't know that `__file__` is a string (at runtime it may be entirely # unset, but we follow typeshed here): diff --git "a/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_\342\200\246_(c3a523878447af6b).snap" "b/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_\342\200\246_(c3a523878447af6b).snap" index 6f834b61e3637..3c1f175437d97 100644 --- "a/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_\342\200\246_(c3a523878447af6b).snap" +++ "b/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_\342\200\246_(c3a523878447af6b).snap" @@ -37,7 +37,7 @@ error[invalid-return-type]: Return type does not match returned value | --- Expected `int` because of return type 2 | # error: [invalid-return-type] 3 | return ... - | ^^^ expected `int`, found `ellipsis` + | ^^^ expected `int`, found `EllipsisType` 4 | 5 | # error: [invalid-return-type] | diff --git a/crates/ty_python_semantic/resources/mdtest/stubs/ellipsis.md b/crates/ty_python_semantic/resources/mdtest/stubs/ellipsis.md index 6b147fca578d1..a29277516d37f 100644 --- a/crates/ty_python_semantic/resources/mdtest/stubs/ellipsis.md +++ b/crates/ty_python_semantic/resources/mdtest/stubs/ellipsis.md @@ -47,7 +47,7 @@ Iterating over an ellipsis literal as part of a `for` loop in a stub is invalid, results in a diagnostic: ```pyi -# error: [not-iterable] "Object of type `ellipsis` is not iterable" +# error: [not-iterable] "Object of type `EllipsisType` is not iterable" for a, b in ...: reveal_type(a) # revealed: Unknown reveal_type(b) # revealed: Unknown @@ -59,13 +59,13 @@ In a non-stub file, there's no special treatment of ellipsis literals. An ellips be assigned if `EllipsisType` is actually assignable to the annotated type. ```py -# error: 7 [invalid-parameter-default] "Default value of type `ellipsis` is not assignable to annotated parameter type `int`" +# error: 7 [invalid-parameter-default] "Default value of type `EllipsisType` is not assignable to annotated parameter type `int`" def f(x: int = ...) -> None: ... -# error: 1 [invalid-assignment] "Object of type `ellipsis` is not assignable to `int`" +# error: 1 [invalid-assignment] "Object of type `EllipsisType` is not assignable to `int`" a: int = ... b = ... -reveal_type(b) # revealed: ellipsis +reveal_type(b) # revealed: EllipsisType ``` ## Use of `Ellipsis` symbol @@ -73,6 +73,6 @@ reveal_type(b) # revealed: ellipsis There is no special treatment of the builtin name `Ellipsis` in stubs, only of `...` literals. ```pyi -# error: 7 [invalid-parameter-default] "Default value of type `ellipsis` is not assignable to annotated parameter type `int`" +# error: 7 [invalid-parameter-default] "Default value of type `EllipsisType` is not assignable to annotated parameter type `int`" def f(x: int = Ellipsis) -> None: ... ``` diff --git a/ruff.schema.json b/ruff.schema.json index 219f6b46e6ab7..684d9e56ee5f4 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -702,7 +702,7 @@ } }, "target-version": { - "description": "The minimum Python version to target, e.g., when considering automatic code upgrades, like rewriting type annotations. Ruff will not propose changes using features that are not available in the given version.\n\nFor example, to represent supporting Python >=3.10 or ==3.10 specify `target-version = \"py310\"`.\n\nIf you're already using a `pyproject.toml` file, we recommend `project.requires-python` instead, as it's based on Python packaging standards, and will be respected by other tools. For example, Ruff treats the following as identical to `target-version = \"py38\"`:\n\n```toml [project] requires-python = \">=3.8\" ```\n\nIf both are specified, `target-version` takes precedence over `requires-python`. See [_Inferring the Python version_](https://docs.astral.sh/ruff/configuration/#inferring-the-python-version) for a complete description of how the `target-version` is determined when left unspecified.\n\nNote that a stub file can [sometimes make use of a typing feature](https://typing.python.org/en/latest/spec/distributing.html#syntax) before it is available at runtime, as long as the stub does not make use of new *syntax*. For example, a type checker will understand `int | str` in a stub as being a `Union` type annotation, even if the type checker is run using Python 3.9, despite the fact that the `|` operator can only be used to create union types at runtime on Python 3.10+. As such, Ruff will often recommend newer features in a stub file than it would for an equivalent runtime file with the same target version.", + "description": "The minimum Python version to target, e.g., when considering automatic code upgrades, like rewriting type annotations. Ruff will not propose changes using features that are not available in the given version.\n\nFor example, to represent supporting Python >=3.11 or ==3.11 specify `target-version = \"py311\"`.\n\nIf you're already using a `pyproject.toml` file, we recommend `project.requires-python` instead, as it's based on Python packaging standards, and will be respected by other tools. For example, Ruff treats the following as identical to `target-version = \"py38\"`:\n\n```toml [project] requires-python = \">=3.8\" ```\n\nIf both are specified, `target-version` takes precedence over `requires-python`. See [_Inferring the Python version_](https://docs.astral.sh/ruff/configuration/#inferring-the-python-version) for a complete description of how the `target-version` is determined when left unspecified.\n\nNote that a stub file can [sometimes make use of a typing feature](https://typing.python.org/en/latest/spec/distributing.html#syntax) before it is available at runtime, as long as the stub does not make use of new *syntax*. For example, a type checker will understand `int | str` in a stub as being a `Union` type annotation, even if the type checker is run using Python 3.9, despite the fact that the `|` operator can only be used to create union types at runtime on Python 3.10+. As such, Ruff will often recommend newer features in a stub file than it would for an equivalent runtime file with the same target version.", "anyOf": [ { "$ref": "#/definitions/PythonVersion"