From fddc5101ded40cd30e36ab65a6d617446e3b7e2e Mon Sep 17 00:00:00 2001 From: Jkhall81 Date: Tue, 6 Jan 2026 09:01:14 -0700 Subject: [PATCH] feat(ruff): add Python 3.15 support and retire RUF017 --- crates/ruff_linter/src/linter.rs | 19 ++++++++++++++++++- crates/ruff_linter/src/preview.rs | 5 +++++ crates/ruff_linter/src/settings/types.rs | 3 +++ crates/ruff_python_ast/src/python_version.rs | 7 ++++++- docs/configuration.md | 4 ++-- ruff.schema.json | 3 ++- ty.schema.json | 4 ++++ 7 files changed, 40 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index f3880076afd257..3e2f070a0068fd 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -26,6 +26,7 @@ use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens}; use crate::fix::{FixResult, fix_file}; use crate::noqa::add_noqa; use crate::package::PackageRoot; +use crate::preview::is_py315_support_enabled; use crate::registry::Rule; #[cfg(any(feature = "test-rules", test))] use crate::rules::ruff::rules::test_rules::{self, TEST_RULES, TestRule}; @@ -33,7 +34,7 @@ use crate::settings::types::UnsafeFixes; use crate::settings::{LinterSettings, TargetVersion, flags}; use crate::source_kind::SourceKind; use crate::suppression::Suppressions; -use crate::{Locator, directives, fs}; +use crate::{Locator, directives, fs, warn_user_once}; pub(crate) mod float; @@ -450,6 +451,14 @@ pub fn lint_only( ) -> LinterResult { let target_version = settings.resolve_target_version(path); + if matches!(target_version.linter_version(), PythonVersion::PY315) + && !is_py315_support_enabled(settings) + { + warn_user_once!( + "Support for Python 3.15 is under development and may be unstable. 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). @@ -555,6 +564,14 @@ pub fn lint_fix<'a>( let target_version = settings.resolve_target_version(path); + if matches!(target_version.linter_version(), PythonVersion::PY315) + && !is_py315_support_enabled(settings) + { + warn_user_once!( + "Support for Python 3.15 is under development and may be unstable. 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 5c4317205323ab..b28474889e5eee 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -296,3 +296,8 @@ pub(crate) const fn is_s310_resolve_string_literal_bindings_enabled( pub(crate) const fn is_range_suppressions_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() } + +// https://github.com/astral-sh/ruff/pull/22419 +pub(crate) const fn is_py315_support_enabled(settings: &LinterSettings) -> bool { + settings.preview.is_enabled() +} diff --git a/crates/ruff_linter/src/settings/types.rs b/crates/ruff_linter/src/settings/types.rs index 05cd13909b69bf..d2575fea0ab2d2 100644 --- a/crates/ruff_linter/src/settings/types.rs +++ b/crates/ruff_linter/src/settings/types.rs @@ -36,6 +36,7 @@ pub enum PythonVersion { Py312, Py313, Py314, + Py315, } impl Default for PythonVersion { @@ -58,6 +59,7 @@ impl TryFrom for PythonVersion { ast::PythonVersion::PY312 => Ok(Self::Py312), ast::PythonVersion::PY313 => Ok(Self::Py313), ast::PythonVersion::PY314 => Ok(Self::Py314), + ast::PythonVersion::PY315 => Ok(Self::Py315), _ => Err(format!("unrecognized python version {value}")), } } @@ -88,6 +90,7 @@ impl PythonVersion { Self::Py312 => (3, 12), Self::Py313 => (3, 13), Self::Py314 => (3, 14), + Self::Py315 => (3, 15), } } } diff --git a/crates/ruff_python_ast/src/python_version.rs b/crates/ruff_python_ast/src/python_version.rs index 6d472d9ab47558..ccd82de800c9cc 100644 --- a/crates/ruff_python_ast/src/python_version.rs +++ b/crates/ruff_python_ast/src/python_version.rs @@ -35,6 +35,10 @@ impl PythonVersion { major: 3, minor: 14, }; + pub const PY315: PythonVersion = PythonVersion { + major: 3, + minor: 15, + }; pub fn iter() -> impl Iterator { [ @@ -46,6 +50,7 @@ impl PythonVersion { PythonVersion::PY312, PythonVersion::PY313, PythonVersion::PY314, + PythonVersion::PY315, ] .into_iter() } @@ -61,7 +66,7 @@ impl PythonVersion { /// The latest Python version supported in preview pub fn latest_preview() -> Self { - let latest_preview = Self::PY314; + let latest_preview = Self::PY315; debug_assert!(latest_preview >= Self::latest()); latest_preview } diff --git a/docs/configuration.md b/docs/configuration.md index a88f71488fd90c..02b89c4c55b972 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -610,7 +610,7 @@ Options: RUFF_OUTPUT_FILE=] --target-version The minimum Python version that should be supported [possible values: - py37, py38, py39, py310, py311, py312, py313, py314] + py37, py38, py39, py310, py311, py312, py313, py314, py315] --preview Enable preview mode; checks will include unstable rules and fixes. Use `--no-preview` to disable @@ -726,7 +726,7 @@ Options: notebooks, use `--extension ipy:ipynb` --target-version The minimum Python version that should be supported [possible values: - py37, py38, py39, py310, py311, py312, py313, py314] + py37, py38, py39, py310, py311, py312, py313, py314, py315] --preview Enable preview mode; enables unstable formatting. Use `--no-preview` to disable diff --git a/ruff.schema.json b/ruff.schema.json index cc11baaacfb1fc..03fa58c68389b2 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2884,7 +2884,8 @@ "py311", "py312", "py313", - "py314" + "py314", + "py315" ] }, "Quote": { diff --git a/ty.schema.json b/ty.schema.json index 0edef9201b932b..7e1f86ac65405a 100644 --- a/ty.schema.json +++ b/ty.schema.json @@ -312,6 +312,10 @@ { "description": "Python 3.14", "const": "3.14" + }, + { + "description": "Python 3.15", + "const": "3.15" } ] },