Stabilize adding future import via config option (#20277)

Introduced in #19100. Removed gating, updated tests, removed warning(s),
and updated documentation.
This commit is contained in:
Dylan 2025-09-08 09:19:39 -05:00 committed by Brent Westbrook
parent 64fe7d30a3
commit 9ca632c84f
13 changed files with 28 additions and 79 deletions

View file

@ -5780,28 +5780,6 @@ match 42: # invalid-syntax
Ok(()) Ok(())
} }
#[test]
fn future_annotations_preview_warning() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--config", "lint.future-annotations = true"])
.args(["--select", "F"])
.arg("--no-preview")
.arg("-")
.pass_stdin("1"),
@r"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
warning: The `lint.future-annotations` setting will have no effect because `preview` is disabled
",
);
}
#[test] #[test]
fn up045_nested_optional_flatten_all() { fn up045_nested_optional_flatten_all() {
let contents = "\ let contents = "\

View file

@ -207,11 +207,6 @@ pub(crate) const fn is_safe_super_call_with_parameters_fix_enabled(
settings.preview.is_enabled() settings.preview.is_enabled()
} }
// https://github.com/astral-sh/ruff/pull/19100
pub(crate) const fn is_add_future_annotations_imports_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled()
}
// https://github.com/astral-sh/ruff/pull/19851 // https://github.com/astral-sh/ruff/pull/19851
pub(crate) const fn is_maxsplit_without_separator_fix_enabled(settings: &LinterSettings) -> bool { pub(crate) const fn is_maxsplit_without_separator_fix_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled() settings.preview.is_enabled()

View file

@ -66,7 +66,7 @@ impl TypingReference {
} }
// prefer `from __future__ import annotations` to quoting // prefer `from __future__ import annotations` to quoting
if settings.future_annotations() if settings.future_annotations
&& !reference.in_typing_only_annotation() && !reference.in_typing_only_annotation()
&& reference.in_runtime_evaluated_annotation() && reference.in_runtime_evaluated_annotation()
{ {

View file

@ -14,7 +14,6 @@ mod tests {
use test_case::test_case; use test_case::test_case;
use crate::registry::{Linter, Rule}; use crate::registry::{Linter, Rule};
use crate::settings::types::PreviewMode;
use crate::test::{test_path, test_snippet}; use crate::test::{test_path, test_snippet};
use crate::{assert_diagnostics, settings}; use crate::{assert_diagnostics, settings};
@ -86,7 +85,6 @@ mod tests {
Path::new("flake8_type_checking").join(path).as_path(), Path::new("flake8_type_checking").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
future_annotations: true, future_annotations: true,
preview: PreviewMode::Enabled,
// also enable quoting annotations to check the interaction. the future import // also enable quoting annotations to check the interaction. the future import
// should take precedence. // should take precedence.
flake8_type_checking: super::settings::Settings { flake8_type_checking: super::settings::Settings {

View file

@ -38,6 +38,13 @@ use crate::{Fix, FixAvailability, Violation};
/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them /// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
/// as such. /// as such.
/// ///
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would enable an import to be
/// moved into an `if TYPE_CHECKING:` block. This takes precedence over the
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if
/// both settings are enabled.
///
///
/// ## Example /// ## Example
/// ```python /// ```python
/// from __future__ import annotations /// from __future__ import annotations
@ -63,14 +70,6 @@ use crate::{Fix, FixAvailability, Violation};
/// return len(sized) /// return len(sized)
/// ``` /// ```
/// ///
///
/// ## Preview
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would enable an import to be moved into an `if
/// TYPE_CHECKING:` block. This takes precedence over the
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if both settings are
/// enabled.
///
/// ## Options /// ## Options
/// - `lint.flake8-type-checking.quote-annotations` /// - `lint.flake8-type-checking.quote-annotations`
/// - `lint.flake8-type-checking.runtime-evaluated-base-classes` /// - `lint.flake8-type-checking.runtime-evaluated-base-classes`
@ -122,6 +121,12 @@ impl Violation for TypingOnlyFirstPartyImport {
/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them /// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
/// as such. /// as such.
/// ///
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would enable an import to be
/// moved into an `if TYPE_CHECKING:` block. This takes precedence over the
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if
/// both settings are enabled.
///
/// ## Example /// ## Example
/// ```python /// ```python
/// from __future__ import annotations /// from __future__ import annotations
@ -147,13 +152,6 @@ impl Violation for TypingOnlyFirstPartyImport {
/// return len(df) /// return len(df)
/// ``` /// ```
/// ///
/// ## Preview
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would enable an import to be moved into an `if
/// TYPE_CHECKING:` block. This takes precedence over the
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if both settings are
/// enabled.
///
/// ## Options /// ## Options
/// - `lint.flake8-type-checking.quote-annotations` /// - `lint.flake8-type-checking.quote-annotations`
/// - `lint.flake8-type-checking.runtime-evaluated-base-classes` /// - `lint.flake8-type-checking.runtime-evaluated-base-classes`
@ -205,6 +203,12 @@ impl Violation for TypingOnlyThirdPartyImport {
/// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them /// [`lint.flake8-type-checking.runtime-evaluated-decorators`] settings to mark them
/// as such. /// as such.
/// ///
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would enable an import to be
/// moved into an `if TYPE_CHECKING:` block. This takes precedence over the
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if
/// both settings are enabled.
///
/// ## Example /// ## Example
/// ```python /// ```python
/// from __future__ import annotations /// from __future__ import annotations
@ -230,15 +234,6 @@ impl Violation for TypingOnlyThirdPartyImport {
/// return str(path) /// return str(path)
/// ``` /// ```
/// ///
/// ## Preview
///
/// When [preview](https://docs.astral.sh/ruff/preview/) is enabled, if
/// [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would enable an import to be moved into an `if
/// TYPE_CHECKING:` block. This takes precedence over the
/// [`lint.flake8-type-checking.quote-annotations`] setting described above if both settings are
/// enabled.
///
/// ## Options /// ## Options
/// - `lint.flake8-type-checking.quote-annotations` /// - `lint.flake8-type-checking.quote-annotations`
/// - `lint.flake8-type-checking.runtime-evaluated-base-classes` /// - `lint.flake8-type-checking.runtime-evaluated-base-classes`
@ -287,7 +282,7 @@ pub(crate) fn typing_only_runtime_import(
// If we can't add a `__future__` import and in un-strict mode, don't flag typing-only // If we can't add a `__future__` import and in un-strict mode, don't flag typing-only
// imports that are implicitly loaded by way of a valid runtime import. // imports that are implicitly loaded by way of a valid runtime import.
if !checker.settings().future_annotations() if !checker.settings().future_annotations
&& !checker.settings().flake8_type_checking.strict && !checker.settings().flake8_type_checking.strict
&& runtime_imports && runtime_imports
.iter() .iter()

View file

@ -147,7 +147,6 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade").join(path).as_path(), Path::new("pyupgrade").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
preview: PreviewMode::Enabled,
future_annotations: true, future_annotations: true,
..settings::LinterSettings::for_rule(rule_code) ..settings::LinterSettings::for_rule(rule_code)
}, },

View file

@ -102,7 +102,7 @@ impl AlwaysFixableViolation for QuotedAnnotation {
/// UP037 /// UP037
pub(crate) fn quoted_annotation(checker: &Checker, annotation: &str, range: TextRange) { pub(crate) fn quoted_annotation(checker: &Checker, annotation: &str, range: TextRange) {
let add_future_import = checker.settings().future_annotations() let add_future_import = checker.settings().future_annotations
&& checker.semantic().in_runtime_evaluated_annotation(); && checker.semantic().in_runtime_evaluated_annotation();
if !(checker.semantic().in_typing_only_annotation() || add_future_import) { if !(checker.semantic().in_typing_only_annotation() || add_future_import) {

View file

@ -649,7 +649,6 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("ruff").join(path).as_path(), Path::new("ruff").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
preview: PreviewMode::Enabled,
future_annotations: true, future_annotations: true,
unresolved_target_version: PythonVersion::PY39.into(), unresolved_target_version: PythonVersion::PY39.into(),
..settings::LinterSettings::for_rule(rule_code) ..settings::LinterSettings::for_rule(rule_code)

View file

@ -19,6 +19,10 @@ use crate::rules::ruff::typing::type_hint_explicitly_allows_none;
/// Checks for the use of implicit `Optional` in type annotations when the /// Checks for the use of implicit `Optional` in type annotations when the
/// default parameter value is `None`. /// default parameter value is `None`.
/// ///
/// If [`lint.future-annotations`] is set to `true`, `from __future__ import
/// annotations` will be added if doing so would allow using the `|` operator on
/// a Python version before 3.10.
///
/// ## Why is this bad? /// ## Why is this bad?
/// Implicit `Optional` is prohibited by [PEP 484]. It is confusing and /// Implicit `Optional` is prohibited by [PEP 484]. It is confusing and
/// inconsistent with the rest of the type system. /// inconsistent with the rest of the type system.
@ -73,12 +77,6 @@ use crate::rules::ruff::typing::type_hint_explicitly_allows_none;
/// - `target-version` /// - `target-version`
/// - `lint.future-annotations` /// - `lint.future-annotations`
/// ///
/// ## Preview
///
/// When [preview] is enabled, if [`lint.future-annotations`] is set to `true`,
/// `from __future__ import annotations` will be added if doing so would allow using the `|`
/// operator on a Python version before 3.10.
///
/// ## Fix safety /// ## Fix safety
/// ///
/// This fix is always marked as unsafe because it can change the behavior of code that relies on /// This fix is always marked as unsafe because it can change the behavior of code that relies on
@ -217,7 +215,7 @@ pub(crate) fn implicit_optional(checker: &Checker, parameters: &Parameters) {
}; };
let conversion_type = if checker.target_version() >= PythonVersion::PY310 let conversion_type = if checker.target_version() >= PythonVersion::PY310
|| checker.settings().future_annotations() || checker.settings().future_annotations
{ {
ConversionType::BinOpOr ConversionType::BinOpOr
} else { } else {

View file

@ -475,11 +475,6 @@ impl LinterSettings {
.is_match(path) .is_match(path)
.map_or(self.unresolved_target_version, TargetVersion::from) .map_or(self.unresolved_target_version, TargetVersion::from)
} }
pub fn future_annotations(&self) -> bool {
// TODO(brent) we can just access the field directly once this is stabilized.
self.future_annotations && crate::preview::is_add_future_annotations_imports_enabled(self)
}
} }
impl Default for LinterSettings { impl Default for LinterSettings {

View file

@ -257,12 +257,6 @@ impl Configuration {
conflicting_import_settings(&isort, &flake8_import_conventions)?; conflicting_import_settings(&isort, &flake8_import_conventions)?;
let future_annotations = lint.future_annotations.unwrap_or_default(); let future_annotations = lint.future_annotations.unwrap_or_default();
if lint_preview.is_disabled() && future_annotations {
warn_user_once!(
"The `lint.future-annotations` setting will have no effect \
because `preview` is disabled"
);
}
Ok(Settings { Ok(Settings {
cache_dir: self cache_dir: self

View file

@ -537,8 +537,6 @@ pub struct LintOptions {
/// For example, `TC001`, `TC002`, and `TC003` can move more imports into `TYPE_CHECKING` blocks /// For example, `TC001`, `TC002`, and `TC003` can move more imports into `TYPE_CHECKING` blocks
/// if `__future__` annotations are enabled. /// if `__future__` annotations are enabled.
/// ///
/// This setting is currently in [preview](https://docs.astral.sh/ruff/preview/) and requires
/// preview mode to be enabled to have any effect.
#[option( #[option(
default = "false", default = "false",
value_type = "bool", value_type = "bool",

2
ruff.schema.json generated
View file

@ -2291,7 +2291,7 @@
] ]
}, },
"future-annotations": { "future-annotations": {
"description": "Whether to allow rules to add `from __future__ import annotations` in cases where this would simplify a fix or enable a new diagnostic.\n\nFor example, `TC001`, `TC002`, and `TC003` can move more imports into `TYPE_CHECKING` blocks if `__future__` annotations are enabled.\n\nThis setting is currently in [preview](https://docs.astral.sh/ruff/preview/) and requires preview mode to be enabled to have any effect.", "description": "Whether to allow rules to add `from __future__ import annotations` in cases where this would simplify a fix or enable a new diagnostic.\n\nFor example, `TC001`, `TC002`, and `TC003` can move more imports into `TYPE_CHECKING` blocks if `__future__` annotations are enabled.",
"type": [ "type": [
"boolean", "boolean",
"null" "null"