From 88c0ce3e389a217dc596374f154e0c1e88b483ad Mon Sep 17 00:00:00 2001 From: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:23:11 -0400 Subject: [PATCH] Update default and latest Python versions for 3.14 (#20725) Summary -- Closes #19467 and also removes the warning about using Python 3.14 without preview enabled. I also bumped `PythonVersion::default` to 3.9 because it reaches EOL this month, but we could also defer that for now if we wanted. The first three commits are related to the `latest` bump to 3.14; the fourth commit bumps the default to 3.10. Note that this PR also bumps the default Python version for ty to 3.10 because there was a test asserting that it stays in sync with `ast::PythonVersion`. Test Plan -- Existing tests I spot-checked the ecosystem report, and I believe these are all expected. Inbits doesn't specify a target Python version, so I guess we're applying the default. UP007, UP035, and UP045 all use the new default value to emit new diagnostics. --- ...nt__requires_python_ruff_toml_above-2.snap | 5 +- ...lint__requires_python_ruff_toml_above.snap | 5 +- .../test/fixtures/pyflakes/F821_18.py | 2 +- .../test/fixtures/pyflakes/F821_26.py | 8 +- crates/ruff_linter/src/linter.rs | 19 +- crates/ruff_linter/src/preview.rs | 4 - crates/ruff_linter/src/rules/fastapi/mod.rs | 36 +- ...i-redundant-response-model_FAST001.py.snap | 213 +++++++++++ ...-api-unused-path-parameter_FAST003.py.snap | 74 ++++ ...i-redundant-response-model_FAST001.py.snap | 191 ---------- ...-api-unused-path-parameter_FAST003.py.snap | 61 ---- .../src/rules/flake8_builtins/mod.rs | 24 +- ..._flake8_builtins__tests__A003_A003.py.snap | 18 - ...sts__A003_A003.py_builtins_ignorelist.snap | 10 +- ...f_builtin-attribute-shadowing_A003.py.snap | 32 ++ ...uiltins__tests__preview__A003_A003.py.snap | 20 - ...__flake8_pyi__tests__PYI055_PYI055.py.snap | 190 ++++++++++ ...__TC001-TC002-TC003_TC001-3_future.py.snap | 62 ++-- ..._future_import__TC001_TC001_future.py.snap | 46 +-- ...ts__add_future_import__TC002_TC002.py.snap | 29 -- ...mport-in-type-checking-block_quote.py.snap | 20 - ...ping-only-third-party-import_quote.py.snap | 107 ++---- ...ing-only-third-party-import_quote2.py.snap | 61 +--- ...ing-only-third-party-import_quote3.py.snap | 33 +- ...ort-in-type-checking-block_TC004_4.py.snap | 19 +- ...ort-in-type-checking-block_TC004_5.py.snap | 55 --- ...ort-in-type-checking-block_TC004_9.py.snap | 40 -- ...mport-in-type-checking-block_quote.py.snap | 25 -- ...ests__runtime-string-union_TC010_2.py.snap | 33 -- ...ping-only-third-party-import_TC002.py.snap | 29 -- ...ping-only-third-party-import_quote.py.snap | 345 +++++++++++++++++- .../src/rules/flake8_use_pathlib/mod.rs | 25 +- ..._use_pathlib__tests__PTH210_PTH210.py.snap | 83 ----- ...se_pathlib__tests__PTH210_PTH210_1.py.snap | 72 ---- ...invalid-pathlib-with-suffix_PTH210.py.snap | 100 +++++ ...valid-pathlib-with-suffix_PTH210_1.py.snap | 88 +++++ crates/ruff_linter/src/rules/pyflakes/mod.rs | 4 +- ...les__pyflakes__tests__F821_F821_18.py.snap | 10 +- ...ules__pyflakes__tests__F821_F821_2.py.snap | 10 +- ...les__pyflakes__tests__F821_F821_26.py.snap | 42 --- ...__rules__pyupgrade__tests__UP006_2.py.snap | 8 +- ...er__rules__pyupgrade__tests__UP007.py.snap | 34 ++ ...__rules__pyupgrade__tests__UP036_0.py.snap | 61 ++++ ...__rules__pyupgrade__tests__UP036_5.py.snap | 84 +++++ ...__rules__pyupgrade__tests__UP037_1.py.snap | 16 +- ...sts__add_future_annotation_UP037_1.py.snap | 12 +- crates/ruff_linter/src/rules/ruff/mod.rs | 28 +- ...ules__ruff__tests__RUF058_RUF058_0.py.snap | 60 ++- ...nter__rules__ruff__tests__confusables.snap | 8 - ...confusables_deferred_annotations_diff.snap | 19 + ...les__ruff__tests__preview_confusables.snap | 8 - crates/ruff_linter/src/test.rs | 8 +- crates/ruff_python_ast/src/python_version.rs | 5 +- ...@blank_line_before_class_docstring.py.snap | 2 +- .../tests/snapshots/format@docstring.py.snap | 10 +- .../format@docstring_code_examples.py.snap | 20 +- ...ormat@docstring_code_examples_crlf.py.snap | 2 +- ...g_code_examples_dynamic_line_width.py.snap | 8 +- .../format@docstring_tab_indentation.py.snap | 4 +- .../format@expression__bytes.py.snap | 4 +- .../format@expression__fstring.py.snap | 4 +- ...format@expression__fstring_preview.py.snap | 2 +- ...licit_concatenated_string_preserve.py.snap | 2 +- .../format@expression__string.py.snap | 4 +- ...rmat@fmt_on_off__fmt_off_docstring.py.snap | 4 +- .../format@fmt_on_off__indent.py.snap | 6 +- ...at@fmt_on_off__mixed_space_and_tab.py.snap | 6 +- .../format@notebook_docstring.py.snap | 4 +- .../tests/snapshots/format@preview.py.snap | 4 +- .../snapshots/format@quote_style.py.snap | 6 +- ...ormatting__docstring_code_examples.py.snap | 4 +- .../format@range_formatting__indent.py.snap | 6 +- .../format@range_formatting__stub.pyi.snap | 2 +- .../format@skip_magic_trailing_comma.py.snap | 4 +- ...lank_line_after_nested_stub_class.pyi.snap | 2 +- ..._line_after_nested_stub_class_eof.pyi.snap | 2 +- .../tests/snapshots/format@tab_width.py.snap | 6 +- crates/ruff_workspace/src/options.rs | 8 +- crates/ty/docs/cli.md | 1 + crates/ty/src/python_version.rs | 6 +- .../resources/mdtest/attributes.md | 2 +- .../resources/mdtest/binary/classes.md | 5 + .../resources/mdtest/call/getattr_static.md | 2 +- .../resources/mdtest/call/methods.md | 4 +- .../mdtest/scopes/moduletype_attrs.md | 2 +- ...alid_return_type_…_(c3a523878447af6b).snap | 2 +- .../resources/mdtest/stubs/ellipsis.md | 10 +- ruff.schema.json | 2 +- 88 files changed, 1640 insertions(+), 1123 deletions(-) create mode 100644 crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-redundant-response-model_FAST001.py.snap create mode 100644 crates/ruff_linter/src/rules/fastapi/snapshots/ruff_linter__rules__fastapi__tests__deferred_annotations_diff_fast-api-unused-path-parameter_FAST003.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_builtins/snapshots/ruff_linter__rules__flake8_builtins__tests__deferred_annotations_diff_builtin-attribute-shadowing_A003.py.snap create mode 100644 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 create mode 100644 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 create mode 100644 crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__confusables_deferred_annotations_diff.snap 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 f35c93029c..da3eb66afc 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 980eaf47ad..e9ca7bd400 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 8576a1d403..eaae5e5ee3 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 f87819ef80..3eaa0d8a4e 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 @@ CStr2: TypeAlias = Union["C", str] # always okay # 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 339a4f4aa5..56e180bfab 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 a5647d5ea0..37a577e8df 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 f6e48dd4ad..17b3cb663c 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 0000000000..fc36a819d1 --- /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 0000000000..2feae227db --- /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 f1c5682ef4..e3843a16cd 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 a3ea2bf7ce..fb6bde76f8 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 315ab704e7..0ab2764765 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 1444cc7d6a..df35fcb66a 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 ad43e1b0fc..df35fcb66a 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 0000000000..ed283ed65a --- /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 6fd8c6f961..537a64433c 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 15a50a1e1b..8192743812 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 ca004802ad..2d3e377e1f 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 6d579e57c2..12b5aa40f2 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 2d3f306807..d84c1fff21 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 1e6dc27a29..22bae5d08e 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 beccb0c7d9..8efb644122 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 0779cfdfd6..5e0b5862ab 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 4614aecb89..5877229b63 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 fc6eb83923..6c5ead2742 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 98817b85bd..6c5ead2742 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 201e5cc19a..6c5ead2742 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 53e45cce3a..22bae5d08e 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 ab0b2a8952..3588e7b241 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 2d3f306807..d84c1fff21 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 173cb5a2d6..8efb644122 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 f7f6c64a8d..5d0938d5c6 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 d2a76a210e..9a057749d1 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 adb00cbf99..1f523ea194 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 0000000000..3a9523b277 --- /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 0000000000..ee2bdd1988 --- /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 6f3e7bbafe..3dd7edde65 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 a487e4ddb8..95d305eb2b 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 89dd2df81f..d0b409f39e 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 9bb838ffff..121eb3edd0 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 e03ab73fc8..272d7d64e7 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 c050ba4988..d2ce0fd604 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 82253977ca..a198337648 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 ea5a1b487a..98e269b932 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 26631a2904..04fbed2e63 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 fa2af22e0c..04fbed2e63 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 b484095aae..7cdc557841 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 83bc6b8d0c..62756e9e9e 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 4e9ab5604c..27e8db6209 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 0000000000..3411053a0d --- /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 d9c235de30..332cf21fec 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 2ebb244d62..01eb02705e 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 7eab154ad1..2cedb435fd 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 58cfb7e492..60177086ce 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 9ca83e81de..e3117a2e39 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 024d662e0a..26fcfcff6c 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 9b1dc4ba52..c40ab98413 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 7112865500..628910f153 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 a7e5f5d84f..c6736bcfa9 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 0e9e65cf92..7b021391c9 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 c929f160df..40ac13cb24 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 508eda3e94..052758abce 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 a9d9f10827..526d24e02a 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 953b2c34cb..0da0050e34 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 c7775aba83..db1d53e68b 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 8fd14d6634..3a68d9ceae 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 99bde1d1d6..1c52065c5a 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 6018c9fc26..6195b43c21 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 3557b8c478..f268f65783 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 b79b0ead6e..a430dc1bb7 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 0cb72a9fb4..cad5ee4f2a 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 669f8f5eb5..1609cf657e 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 d2ec8f05d8..5610ef79ee 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 12521c9433..e427d077c7 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 1d7b8c7636..a1efd92a9c 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 55485257f3..9e9dc166fb 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 ab1caa6805..51b77d3f16 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 055291b53c..21fc2269eb 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 83de0b3d6d..f9f5580f2b 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 09ce15f526..694e81b55e 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 8f5742bcc7..1c0c2da772 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 89dceef0e2..d0ded68b3d 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 a68bae12d2..a8d87bbfa6 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 f7bc04fba9..7db0259951 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 16240e81d9..b9d92d7fed 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_…_(c3a523878447af6b).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_…_(c3a523878447af6b).snap index 6f834b61e3..3c1f175437 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_…_(c3a523878447af6b).snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/return_type.md_-_Function_return_type_-_Invalid_return_type_…_(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 6b147fca57..a29277516d 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 219f6b46e6..684d9e56ee 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"