diff --git a/README.md b/README.md index 2b2a74ef7d..f248140ef9 100644 --- a/README.md +++ b/README.md @@ -1845,6 +1845,26 @@ has no `self` or `cls` argument. staticmethod-decorators = ["staticmethod", "stcmthd"] ``` +### `pyupgrade` + +#### [`keep_runtime_typing`](#keep_runtime_typing) + +Whether to avoid PEP 585 (`List[int]` -> `list[int]`) and PEP 604 (`Optional[str]` -> `str | None`) +rewrites even if a file imports `from __future__ import annotations`. Note that this setting is +only applicable when the target Python version is below 3.9 and 3.10 respectively. + +**Default value**: `false` + +**Type**: `bool` + +**Example usage**: + +```toml +[tool.ruff.pep8-naming] +# Preserve types, even if a file imports `from __future__ import annotations`. +keep-runtime-typing = true +``` + ## License MIT diff --git a/pyproject.toml b/pyproject.toml index 662e2209ae..103b9ed453 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,3 +35,6 @@ strip = true [tool.isort] profile = "black" known_third_party = ["fastapi", "pydantic", "starlette"] + +[tool.ruff.pyupgrade] + diff --git a/src/check_ast.rs b/src/check_ast.rs index ac4a4cfb36..0aba56e507 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -1197,6 +1197,7 @@ where && self.settings.enabled.contains(&CheckCode::U007) && (self.settings.target_version >= PythonVersion::Py310 || (self.settings.target_version >= PythonVersion::Py37 + && !self.settings.pyupgrade.keep_runtime_typing && self.annotations_future_enabled && self.in_deferred_annotation)) { @@ -1239,6 +1240,7 @@ where && self.settings.enabled.contains(&CheckCode::U006) && (self.settings.target_version >= PythonVersion::Py39 || (self.settings.target_version >= PythonVersion::Py37 + && !self.settings.pyupgrade.keep_runtime_typing && self.annotations_future_enabled && self.in_deferred_annotation)) && typing::is_pep585_builtin( diff --git a/src/flake8_bugbear/settings.rs b/src/flake8_bugbear/settings.rs index 8bf0422aa5..45784f5d66 100644 --- a/src/flake8_bugbear/settings.rs +++ b/src/flake8_bugbear/settings.rs @@ -1,4 +1,4 @@ -//! Settings for the `pep8-naming` plugin. +//! Settings for the `flake8-bugbear` plugin. use serde::{Deserialize, Serialize}; diff --git a/src/pyupgrade/mod.rs b/src/pyupgrade/mod.rs index 1ed9ca7914..29b7271728 100644 --- a/src/pyupgrade/mod.rs +++ b/src/pyupgrade/mod.rs @@ -1,4 +1,5 @@ mod checks; pub mod fixes; pub mod plugins; +pub mod settings; pub mod types; diff --git a/src/pyupgrade/settings.rs b/src/pyupgrade/settings.rs new file mode 100644 index 0000000000..fe55d460ec --- /dev/null +++ b/src/pyupgrade/settings.rs @@ -0,0 +1,22 @@ +//! Settings for the `pyupgrade` plugin. + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(deny_unknown_fields, rename_all = "kebab-case")] +pub struct Options { + pub keep_runtime_typing: Option, +} + +#[derive(Debug, Hash, Default)] +pub struct Settings { + pub keep_runtime_typing: bool, +} + +impl Settings { + pub fn from_options(options: Options) -> Self { + Self { + keep_runtime_typing: options.keep_runtime_typing.unwrap_or_default(), + } + } +} diff --git a/src/settings/configuration.rs b/src/settings/configuration.rs index 56704e0329..0cd4621650 100644 --- a/src/settings/configuration.rs +++ b/src/settings/configuration.rs @@ -14,7 +14,7 @@ use crate::settings::pyproject::load_options; use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion, SerializationFormat}; use crate::{ flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, fs, isort, mccabe, - pep8_naming, + pep8_naming, pyupgrade, }; #[derive(Debug)] @@ -44,6 +44,7 @@ pub struct Configuration { pub isort: isort::settings::Settings, pub mccabe: mccabe::settings::Settings, pub pep8_naming: pep8_naming::settings::Settings, + pub pyupgrade: pyupgrade::settings::Settings, } static DEFAULT_EXCLUDE: Lazy> = Lazy::new(|| { @@ -164,6 +165,10 @@ impl Configuration { .pep8_naming .map(pep8_naming::settings::Settings::from_options) .unwrap_or_default(), + pyupgrade: options + .pyupgrade + .map(pyupgrade::settings::Settings::from_options) + .unwrap_or_default(), }) } } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 60b5d92504..e81973ce45 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -18,7 +18,7 @@ use crate::settings::configuration::Configuration; use crate::settings::types::{FilePattern, PerFileIgnore, PythonVersion, SerializationFormat}; use crate::{ flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, fs, isort, mccabe, - pep8_naming, + pep8_naming, pyupgrade, }; pub mod configuration; @@ -48,6 +48,7 @@ pub struct Settings { pub isort: isort::settings::Settings, pub mccabe: mccabe::settings::Settings, pub pep8_naming: pep8_naming::settings::Settings, + pub pyupgrade: pyupgrade::settings::Settings, } impl Settings { @@ -82,6 +83,7 @@ impl Settings { mccabe: config.mccabe, line_length: config.line_length, pep8_naming: config.pep8_naming, + pyupgrade: config.pyupgrade, per_file_ignores: resolve_per_file_ignores(config.per_file_ignores, project_root)?, src: config.src, target_version: config.target_version, @@ -110,6 +112,7 @@ impl Settings { isort: isort::settings::Settings::default(), mccabe: mccabe::settings::Settings::default(), pep8_naming: pep8_naming::settings::Settings::default(), + pyupgrade: pyupgrade::settings::Settings::default(), } } @@ -134,6 +137,7 @@ impl Settings { isort: isort::settings::Settings::default(), mccabe: mccabe::settings::Settings::default(), pep8_naming: pep8_naming::settings::Settings::default(), + pyupgrade: pyupgrade::settings::Settings::default(), } } } @@ -165,6 +169,7 @@ impl Hash for Settings { self.isort.hash(state); self.mccabe.hash(state); self.pep8_naming.hash(state); + self.pyupgrade.hash(state); } } diff --git a/src/settings/options.rs b/src/settings/options.rs index 57a83e74c0..fb016227c5 100644 --- a/src/settings/options.rs +++ b/src/settings/options.rs @@ -7,7 +7,7 @@ use crate::checks_gen::CheckCodePrefix; use crate::settings::types::{PythonVersion, SerializationFormat}; use crate::{ flake8_annotations, flake8_bugbear, flake8_quotes, flake8_tidy_imports, isort, mccabe, - pep8_naming, + pep8_naming, pyupgrade, }; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)] @@ -37,6 +37,7 @@ pub struct Options { pub isort: Option, pub mccabe: Option, pub pep8_naming: Option, + pub pyupgrade: Option, // Tables are required to go last. pub per_file_ignores: Option>>, }