Use ast::PythonVersion internally in the formatter and linter (#16170)

## Summary

This PR updates the formatter and linter to use the `PythonVersion`
struct from the `ruff_python_ast` crate internally. While this doesn't
remove the need for the `linter::PythonVersion` enum, it does remove the
`formatter::PythonVersion` enum and limits the use in the linter to
deserializing from CLI arguments and config files and moves most of the
remaining methods to the `ast::PythonVersion` struct.

## Test Plan

Existing tests, with some inputs and outputs updated to reflect the new
(de)serialization format. I think these are test-specific and shouldn't
affect any external (de)serialization.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Brent Westbrook 2025-02-18 12:03:13 -05:00 committed by GitHub
parent 0868e73d2c
commit a9efdea113
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
153 changed files with 456 additions and 539 deletions

View file

@ -40,7 +40,7 @@ impl std::fmt::Display for PythonVersion {
} }
} }
impl From<PythonVersion> for ruff_python_ast::python_version::PythonVersion { impl From<PythonVersion> for ruff_python_ast::PythonVersion {
fn from(value: PythonVersion) -> Self { fn from(value: PythonVersion) -> Self {
match value { match value {
PythonVersion::Py37 => Self::PY37, PythonVersion::Py37 => Self::PY37,
@ -61,8 +61,8 @@ mod tests {
#[test] #[test]
fn same_default_as_python_version() { fn same_default_as_python_version() {
assert_eq!( assert_eq!(
ruff_python_ast::python_version::PythonVersion::from(PythonVersion::default()), ruff_python_ast::PythonVersion::from(PythonVersion::default()),
ruff_python_ast::python_version::PythonVersion::default() ruff_python_ast::PythonVersion::default()
); );
} }
} }

View file

@ -16,7 +16,7 @@ use ruff_db::system::{
OsSystem, System, SystemPath, SystemPathBuf, UserConfigDirectoryOverrideGuard, OsSystem, System, SystemPath, SystemPathBuf, UserConfigDirectoryOverrideGuard,
}; };
use ruff_db::Upcast; use ruff_db::Upcast;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
struct TestCase { struct TestCase {
db: ProjectDatabase, db: ProjectDatabase,

View file

@ -2,7 +2,7 @@ use std::{collections::HashMap, hash::BuildHasher};
use red_knot_python_semantic::{PythonPlatform, SitePackages}; use red_knot_python_semantic::{PythonPlatform, SitePackages};
use ruff_db::system::SystemPathBuf; use ruff_db::system::SystemPathBuf;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
/// Combine two values, preferring the values in `self`. /// Combine two values, preferring the values in `self`.
/// ///

View file

@ -310,7 +310,7 @@ mod tests {
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use insta::assert_ron_snapshot; use insta::assert_ron_snapshot;
use ruff_db::system::{SystemPathBuf, TestSystem}; use ruff_db::system::{SystemPathBuf, TestSystem};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use crate::{ProjectDiscoveryError, ProjectMetadata}; use crate::{ProjectDiscoveryError, ProjectMetadata};

View file

@ -6,7 +6,7 @@ use ruff_db::diagnostic::{Diagnostic, DiagnosticId, Severity, Span};
use ruff_db::files::system_path_to_file; use ruff_db::files::system_path_to_file;
use ruff_db::system::{System, SystemPath}; use ruff_db::system::{System, SystemPath};
use ruff_macros::Combine; use ruff_macros::Combine;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::borrow::Cow; use std::borrow::Cow;

View file

@ -1,7 +1,7 @@
use crate::metadata::options::Options; use crate::metadata::options::Options;
use crate::metadata::value::{RangedValue, ValueSource, ValueSourceGuard}; use crate::metadata::value::{RangedValue, ValueSource, ValueSourceGuard};
use pep440_rs::{release_specifiers_to_ranges, Version, VersionSpecifiers}; use pep440_rs::{release_specifiers_to_ranges, Version, VersionSpecifiers};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use std::collections::Bound; use std::collections::Bound;
use std::ops::Deref; use std::ops::Deref;

View file

@ -28,7 +28,7 @@ pub(crate) mod tests {
use ruff_db::system::{DbWithTestSystem, System, SystemPathBuf, TestSystem}; use ruff_db::system::{DbWithTestSystem, System, SystemPathBuf, TestSystem};
use ruff_db::vendored::VendoredFileSystem; use ruff_db::vendored::VendoredFileSystem;
use ruff_db::{Db as SourceDb, Upcast}; use ruff_db::{Db as SourceDb, Upcast};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
#[salsa::db] #[salsa::db]
#[derive(Clone)] #[derive(Clone)]

View file

@ -631,7 +631,7 @@ impl PartialEq<SearchPath> for VendoredPathBuf {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ruff_db::Db; use ruff_db::Db;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use crate::db::tests::TestDb; use crate::db::tests::TestDb;
use crate::module_resolver::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder}; use crate::module_resolver::testing::{FileSpec, MockedTypeshed, TestCase, TestCaseBuilder};

View file

@ -6,7 +6,7 @@ use rustc_hash::{FxBuildHasher, FxHashSet};
use ruff_db::files::{File, FilePath, FileRootKind}; use ruff_db::files::{File, FilePath, FileRootKind};
use ruff_db::system::{DirectoryEntry, System, SystemPath, SystemPathBuf}; use ruff_db::system::{DirectoryEntry, System, SystemPath, SystemPathBuf};
use ruff_db::vendored::{VendoredFileSystem, VendoredPath}; use ruff_db::vendored::{VendoredFileSystem, VendoredPath};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use crate::db::Db; use crate::db::Db;
use crate::module_name::ModuleName; use crate::module_name::ModuleName;
@ -725,7 +725,7 @@ mod tests {
assert_const_function_query_was_not_run, assert_function_query_was_not_run, assert_const_function_query_was_not_run, assert_function_query_was_not_run,
}; };
use ruff_db::Db; use ruff_db::Db;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use crate::db::tests::TestDb; use crate::db::tests::TestDb;
use crate::module_name::ModuleName; use crate::module_name::ModuleName;

View file

@ -1,6 +1,6 @@
use ruff_db::system::{DbWithTestSystem, SystemPath, SystemPathBuf}; use ruff_db::system::{DbWithTestSystem, SystemPath, SystemPathBuf};
use ruff_db::vendored::VendoredPathBuf; use ruff_db::vendored::VendoredPathBuf;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use crate::db::tests::TestDb; use crate::db::tests::TestDb;
use crate::program::{Program, SearchPathSettings}; use crate::program::{Program, SearchPathSettings};

View file

@ -4,7 +4,7 @@ use std::num::{NonZeroU16, NonZeroUsize};
use std::ops::{RangeFrom, RangeInclusive}; use std::ops::{RangeFrom, RangeInclusive};
use std::str::FromStr; use std::str::FromStr;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::db::Db; use crate::db::Db;

View file

@ -4,7 +4,7 @@ use crate::Db;
use anyhow::Context; use anyhow::Context;
use ruff_db::system::{SystemPath, SystemPathBuf}; use ruff_db::system::{SystemPath, SystemPathBuf};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use salsa::Durability; use salsa::Durability;
use salsa::Setter; use salsa::Setter;

View file

@ -14,7 +14,7 @@ use std::num::NonZeroUsize;
use std::ops::Deref; use std::ops::Deref;
use ruff_db::system::{System, SystemPath, SystemPathBuf}; use ruff_db::system::{System, SystemPath, SystemPathBuf};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
type SitePackagesDiscoveryResult<T> = Result<T, SitePackagesDiscoveryError>; type SitePackagesDiscoveryResult<T> = Result<T, SitePackagesDiscoveryError>;

View file

@ -8,7 +8,7 @@ use indexmap::IndexSet;
use itertools::Itertools; use itertools::Itertools;
use ruff_db::files::File; use ruff_db::files::File;
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use type_ordering::union_elements_ordering; use type_ordering::union_elements_ordering;
pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder}; pub(crate) use self::builder::{IntersectionBuilder, UnionBuilder};
@ -4535,7 +4535,7 @@ pub(crate) mod tests {
use ruff_db::parsed::parsed_module; use ruff_db::parsed::parsed_module;
use ruff_db::system::DbWithTestSystem; use ruff_db::system::DbWithTestSystem;
use ruff_db::testing::assert_function_query_was_not_run; use ruff_db::testing::assert_function_query_was_not_run;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use test_case::test_case; use test_case::test_case;
/// Explicitly test for Python version <3.13 and >=3.13, to ensure that /// Explicitly test for Python version <3.13 and >=3.13, to ensure that

View file

@ -10,7 +10,7 @@
use anyhow::Context; use anyhow::Context;
use red_knot_python_semantic::PythonPlatform; use red_knot_python_semantic::PythonPlatform;
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize, Debug, Default, Clone)] #[derive(Deserialize, Debug, Default, Clone)]

View file

@ -9,7 +9,7 @@ use ruff_db::files::{File, Files};
use ruff_db::system::{DbWithTestSystem, System, SystemPath, SystemPathBuf, TestSystem}; use ruff_db::system::{DbWithTestSystem, System, SystemPath, SystemPathBuf, TestSystem};
use ruff_db::vendored::VendoredFileSystem; use ruff_db::vendored::VendoredFileSystem;
use ruff_db::{Db as SourceDb, Upcast}; use ruff_db::{Db as SourceDb, Upcast};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
#[salsa::db] #[salsa::db]
#[derive(Clone)] #[derive(Clone)]

View file

@ -198,7 +198,7 @@ pub enum PythonVersion {
Py313, Py313,
} }
impl From<PythonVersion> for ruff_python_ast::python_version::PythonVersion { impl From<PythonVersion> for ruff_python_ast::PythonVersion {
fn from(value: PythonVersion) -> Self { fn from(value: PythonVersion) -> Self {
match value { match value {
PythonVersion::Py37 => Self::PY37, PythonVersion::Py37 => Self::PY37,
@ -308,8 +308,8 @@ mod tests {
#[test] #[test]
fn same_default_as_python_version() { fn same_default_as_python_version() {
assert_eq!( assert_eq!(
ruff_python_ast::python_version::PythonVersion::from(PythonVersion::default()), ruff_python_ast::PythonVersion::from(PythonVersion::default()),
ruff_python_ast::python_version::PythonVersion::default() ruff_python_ast::PythonVersion::default()
); );
} }
} }

View file

@ -21,6 +21,7 @@ use ruff_linter::settings::types::{
PythonVersion, UnsafeFixes, PythonVersion, UnsafeFixes,
}; };
use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser}; use ruff_linter::{RuleParser, RuleSelector, RuleSelectorParser};
use ruff_python_ast as ast;
use ruff_source_file::{LineIndex, OneIndexed}; use ruff_source_file::{LineIndex, OneIndexed};
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use ruff_workspace::configuration::{Configuration, RuleSelection}; use ruff_workspace::configuration::{Configuration, RuleSelection};
@ -728,7 +729,7 @@ impl CheckCommand {
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from), preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
respect_gitignore: resolve_bool_arg(self.respect_gitignore, self.no_respect_gitignore), respect_gitignore: resolve_bool_arg(self.respect_gitignore, self.no_respect_gitignore),
select: self.select, select: self.select,
target_version: self.target_version, target_version: self.target_version.map(ast::PythonVersion::from),
unfixable: self.unfixable, unfixable: self.unfixable,
// TODO(charlie): Included in `pyproject.toml`, but not inherited. // TODO(charlie): Included in `pyproject.toml`, but not inherited.
cache_dir: self.cache_dir, cache_dir: self.cache_dir,
@ -770,7 +771,7 @@ impl FormatCommand {
exclude: self.exclude, exclude: self.exclude,
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from), preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude), force_exclude: resolve_bool_arg(self.force_exclude, self.no_force_exclude),
target_version: self.target_version, target_version: self.target_version.map(ast::PythonVersion::from),
cache_dir: self.cache_dir, cache_dir: self.cache_dir,
extension: self.extension, extension: self.extension,
..ExplicitConfigOverrides::default() ..ExplicitConfigOverrides::default()
@ -800,7 +801,7 @@ impl AnalyzeGraphCommand {
None None
}, },
preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from), preview: resolve_bool_arg(self.preview, self.no_preview).map(PreviewMode::from),
target_version: self.target_version, target_version: self.target_version.map(ast::PythonVersion::from),
..ExplicitConfigOverrides::default() ..ExplicitConfigOverrides::default()
}; };
@ -1264,7 +1265,7 @@ struct ExplicitConfigOverrides {
preview: Option<PreviewMode>, preview: Option<PreviewMode>,
respect_gitignore: Option<bool>, respect_gitignore: Option<bool>,
select: Option<Vec<RuleSelector>>, select: Option<Vec<RuleSelector>>,
target_version: Option<PythonVersion>, target_version: Option<ast::PythonVersion>,
unfixable: Option<Vec<RuleSelector>>, unfixable: Option<Vec<RuleSelector>>,
// TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`. // TODO(charlie): Captured in pyproject.toml as a default, but not part of `Settings`.
cache_dir: Option<PathBuf>, cache_dir: Option<PathBuf>,

View file

@ -189,7 +189,7 @@ linter.rules.should_fix = [
linter.per_file_ignores = {} linter.per_file_ignores = {}
linter.safety_table.forced_safe = [] linter.safety_table.forced_safe = []
linter.safety_table.forced_unsafe = [] linter.safety_table.forced_unsafe = []
linter.target_version = Py37 linter.target_version = 3.7
linter.preview = disabled linter.preview = disabled
linter.explicit_preview_rules = false linter.explicit_preview_rules = false
linter.extension = ExtensionMapping({}) linter.extension = ExtensionMapping({})
@ -373,7 +373,7 @@ linter.ruff.allowed_markup_calls = []
# Formatter Settings # Formatter Settings
formatter.exclude = [] formatter.exclude = []
formatter.target_version = Py37 formatter.target_version = 3.7
formatter.preview = disabled formatter.preview = disabled
formatter.line_width = 100 formatter.line_width = 100
formatter.line_ending = auto formatter.line_ending = auto
@ -387,7 +387,7 @@ formatter.docstring_code_line_width = dynamic
# Analyze Settings # Analyze Settings
analyze.exclude = [] analyze.exclude = []
analyze.preview = disabled analyze.preview = disabled
analyze.target_version = Py37 analyze.target_version = 3.7
analyze.detect_string_imports = false analyze.detect_string_imports = false
analyze.extension = ExtensionMapping({}) analyze.extension = ExtensionMapping({})
analyze.include_dependencies = {} analyze.include_dependencies = {}

View file

@ -14,7 +14,7 @@ use ruff_db::diagnostic::{Diagnostic, DiagnosticId, Severity};
use ruff_db::files::{system_path_to_file, File}; use ruff_db::files::{system_path_to_file, File};
use ruff_db::source::source_text; use ruff_db::source::source_text;
use ruff_db::system::{MemoryFileSystem, SystemPath, SystemPathBuf, TestSystem}; use ruff_db::system::{MemoryFileSystem, SystemPath, SystemPathBuf, TestSystem};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
struct Case { struct Case {

View file

@ -10,7 +10,7 @@ use ruff_db::files::{File, Files};
use ruff_db::system::{OsSystem, System, SystemPathBuf}; use ruff_db::system::{OsSystem, System, SystemPathBuf};
use ruff_db::vendored::{VendoredFileSystem, VendoredFileSystemBuilder}; use ruff_db::vendored::{VendoredFileSystem, VendoredFileSystemBuilder};
use ruff_db::{Db as SourceDb, Upcast}; use ruff_db::{Db as SourceDb, Upcast};
use ruff_python_ast::python_version::PythonVersion; use ruff_python_ast::PythonVersion;
static EMPTY_VENDORED: std::sync::LazyLock<VendoredFileSystem> = std::sync::LazyLock::new(|| { static EMPTY_VENDORED: std::sync::LazyLock<VendoredFileSystem> = std::sync::LazyLock::new(|| {
let mut builder = VendoredFileSystemBuilder::new(CompressionMethod::Stored); let mut builder = VendoredFileSystemBuilder::new(CompressionMethod::Stored);

View file

@ -1,6 +1,7 @@
use ruff_linter::display_settings; use ruff_linter::display_settings;
use ruff_linter::settings::types::{ExtensionMapping, FilePatternSet, PreviewMode, PythonVersion}; use ruff_linter::settings::types::{ExtensionMapping, FilePatternSet, PreviewMode};
use ruff_macros::CacheKey; use ruff_macros::CacheKey;
use ruff_python_ast::PythonVersion;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
@ -24,7 +25,7 @@ impl fmt::Display for AnalyzeSettings {
fields = [ fields = [
self.exclude, self.exclude,
self.preview, self.preview,
self.target_version | debug, self.target_version,
self.detect_string_imports, self.detect_string_imports,
self.extension | debug, self.extension | debug,
self.include_dependencies | debug, self.include_dependencies | debug,

View file

@ -18,7 +18,7 @@ use crate::rules::{
flake8_simplify, flake8_tidy_imports, flake8_type_checking, flake8_use_pathlib, flynt, numpy, flake8_simplify, flake8_tidy_imports, flake8_type_checking, flake8_use_pathlib, flynt, numpy,
pandas_vet, pep8_naming, pycodestyle, pyflakes, pylint, pyupgrade, refurb, ruff, pandas_vet, pep8_naming, pycodestyle, pyflakes, pylint, pyupgrade, refurb, ruff,
}; };
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// Run lint rules over an [`Expr`] syntax node. /// Run lint rules over an [`Expr`] syntax node.
pub(crate) fn expression(expr: &Expr, checker: &Checker) { pub(crate) fn expression(expr: &Expr, checker: &Checker) {
@ -34,8 +34,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
{ {
if checker.enabled(Rule::FutureRewritableTypeAnnotation) { if checker.enabled(Rule::FutureRewritableTypeAnnotation) {
if !checker.semantic.future_annotations_or_stub() if !checker.semantic.future_annotations_or_stub()
&& checker.settings.target_version < PythonVersion::Py310 && checker.settings.target_version < PythonVersion::PY310
&& checker.settings.target_version >= PythonVersion::Py37 && checker.settings.target_version >= PythonVersion::PY37
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& !checker.settings.pyupgrade.keep_runtime_typing && !checker.settings.pyupgrade.keep_runtime_typing
{ {
@ -49,8 +49,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
Rule::NonPEP604AnnotationOptional, Rule::NonPEP604AnnotationOptional,
]) { ]) {
if checker.source_type.is_stub() if checker.source_type.is_stub()
|| checker.settings.target_version >= PythonVersion::Py310 || checker.settings.target_version >= PythonVersion::PY310
|| (checker.settings.target_version >= PythonVersion::Py37 || (checker.settings.target_version >= PythonVersion::PY37
&& checker.semantic.future_annotations_or_stub() && checker.semantic.future_annotations_or_stub()
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& !checker.settings.pyupgrade.keep_runtime_typing) && !checker.settings.pyupgrade.keep_runtime_typing)
@ -64,7 +64,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
// Ex) list[...] // Ex) list[...]
if checker.enabled(Rule::FutureRequiredTypeAnnotation) { if checker.enabled(Rule::FutureRequiredTypeAnnotation) {
if !checker.semantic.future_annotations_or_stub() if !checker.semantic.future_annotations_or_stub()
&& checker.settings.target_version < PythonVersion::Py39 && checker.settings.target_version < PythonVersion::PY39
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& checker.semantic.in_runtime_evaluated_annotation() && checker.semantic.in_runtime_evaluated_annotation()
&& !checker.semantic.in_string_type_definition() && !checker.semantic.in_string_type_definition()
@ -135,7 +135,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
} }
if checker.enabled(Rule::UnnecessaryDefaultTypeArgs) { if checker.enabled(Rule::UnnecessaryDefaultTypeArgs) {
if checker.settings.target_version >= PythonVersion::Py313 { if checker.settings.target_version >= PythonVersion::PY313 {
pyupgrade::rules::unnecessary_default_type_args(checker, expr); pyupgrade::rules::unnecessary_default_type_args(checker, expr);
} }
} }
@ -268,8 +268,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
{ {
if checker.enabled(Rule::FutureRewritableTypeAnnotation) { if checker.enabled(Rule::FutureRewritableTypeAnnotation) {
if !checker.semantic.future_annotations_or_stub() if !checker.semantic.future_annotations_or_stub()
&& checker.settings.target_version < PythonVersion::Py39 && checker.settings.target_version < PythonVersion::PY39
&& checker.settings.target_version >= PythonVersion::Py37 && checker.settings.target_version >= PythonVersion::PY37
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& !checker.settings.pyupgrade.keep_runtime_typing && !checker.settings.pyupgrade.keep_runtime_typing
{ {
@ -278,8 +278,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
} }
if checker.enabled(Rule::NonPEP585Annotation) { if checker.enabled(Rule::NonPEP585Annotation) {
if checker.source_type.is_stub() if checker.source_type.is_stub()
|| checker.settings.target_version >= PythonVersion::Py39 || checker.settings.target_version >= PythonVersion::PY39
|| (checker.settings.target_version >= PythonVersion::Py37 || (checker.settings.target_version >= PythonVersion::PY37
&& checker.semantic.future_annotations_or_stub() && checker.semantic.future_annotations_or_stub()
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& !checker.settings.pyupgrade.keep_runtime_typing) && !checker.settings.pyupgrade.keep_runtime_typing)
@ -378,8 +378,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
if let Some(replacement) = typing::to_pep585_generic(expr, &checker.semantic) { if let Some(replacement) = typing::to_pep585_generic(expr, &checker.semantic) {
if checker.enabled(Rule::FutureRewritableTypeAnnotation) { if checker.enabled(Rule::FutureRewritableTypeAnnotation) {
if !checker.semantic.future_annotations_or_stub() if !checker.semantic.future_annotations_or_stub()
&& checker.settings.target_version < PythonVersion::Py39 && checker.settings.target_version < PythonVersion::PY39
&& checker.settings.target_version >= PythonVersion::Py37 && checker.settings.target_version >= PythonVersion::PY37
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& !checker.settings.pyupgrade.keep_runtime_typing && !checker.settings.pyupgrade.keep_runtime_typing
{ {
@ -390,8 +390,8 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
} }
if checker.enabled(Rule::NonPEP585Annotation) { if checker.enabled(Rule::NonPEP585Annotation) {
if checker.source_type.is_stub() if checker.source_type.is_stub()
|| checker.settings.target_version >= PythonVersion::Py39 || checker.settings.target_version >= PythonVersion::PY39
|| (checker.settings.target_version >= PythonVersion::Py37 || (checker.settings.target_version >= PythonVersion::PY37
&& checker.semantic.future_annotations_or_stub() && checker.semantic.future_annotations_or_stub()
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& !checker.settings.pyupgrade.keep_runtime_typing) && !checker.settings.pyupgrade.keep_runtime_typing)
@ -405,7 +405,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
refurb::rules::regex_flag_alias(checker, expr); refurb::rules::regex_flag_alias(checker, expr);
} }
if checker.enabled(Rule::DatetimeTimezoneUTC) { if checker.enabled(Rule::DatetimeTimezoneUTC) {
if checker.settings.target_version >= PythonVersion::Py311 { if checker.settings.target_version >= PythonVersion::PY311 {
pyupgrade::rules::datetime_utc_alias(checker, expr); pyupgrade::rules::datetime_utc_alias(checker, expr);
} }
} }
@ -610,12 +610,12 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
pyupgrade::rules::os_error_alias_call(checker, func); pyupgrade::rules::os_error_alias_call(checker, func);
} }
if checker.enabled(Rule::TimeoutErrorAlias) { if checker.enabled(Rule::TimeoutErrorAlias) {
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
pyupgrade::rules::timeout_error_alias_call(checker, func); pyupgrade::rules::timeout_error_alias_call(checker, func);
} }
} }
if checker.enabled(Rule::NonPEP604Isinstance) { if checker.enabled(Rule::NonPEP604Isinstance) {
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
pyupgrade::rules::use_pep604_isinstance(checker, expr, func, args); pyupgrade::rules::use_pep604_isinstance(checker, expr, func, args);
} }
} }
@ -690,7 +690,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
); );
} }
if checker.enabled(Rule::ZipWithoutExplicitStrict) { if checker.enabled(Rule::ZipWithoutExplicitStrict) {
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
flake8_bugbear::rules::zip_without_explicit_strict(checker, call); flake8_bugbear::rules::zip_without_explicit_strict(checker, call);
} }
} }
@ -963,7 +963,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
flake8_pytest_style::rules::fail_call(checker, call); flake8_pytest_style::rules::fail_call(checker, call);
} }
if checker.enabled(Rule::ZipInsteadOfPairwise) { if checker.enabled(Rule::ZipInsteadOfPairwise) {
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
ruff::rules::zip_instead_of_pairwise(checker, call); ruff::rules::zip_instead_of_pairwise(checker, call);
} }
} }
@ -1385,7 +1385,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
// Ex) `str | None` // Ex) `str | None`
if checker.enabled(Rule::FutureRequiredTypeAnnotation) { if checker.enabled(Rule::FutureRequiredTypeAnnotation) {
if !checker.semantic.future_annotations_or_stub() if !checker.semantic.future_annotations_or_stub()
&& checker.settings.target_version < PythonVersion::Py310 && checker.settings.target_version < PythonVersion::PY310
&& checker.semantic.in_annotation() && checker.semantic.in_annotation()
&& checker.semantic.in_runtime_evaluated_annotation() && checker.semantic.in_runtime_evaluated_annotation()
&& !checker.semantic.in_string_type_definition() && !checker.semantic.in_string_type_definition()

View file

@ -14,7 +14,7 @@ use crate::rules::{
flake8_slots, flake8_tidy_imports, flake8_type_checking, mccabe, pandas_vet, pep8_naming, flake8_slots, flake8_tidy_imports, flake8_type_checking, mccabe, pandas_vet, pep8_naming,
perflint, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops, perflint, pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops,
}; };
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// Run lint rules over a [`Stmt`] syntax node. /// Run lint rules over a [`Stmt`] syntax node.
pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
@ -165,7 +165,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} }
} }
if checker.source_type.is_stub() if checker.source_type.is_stub()
|| checker.settings.target_version >= PythonVersion::Py311 || checker.settings.target_version >= PythonVersion::PY311
{ {
if checker.enabled(Rule::NoReturnArgumentAnnotationInStub) { if checker.enabled(Rule::NoReturnArgumentAnnotationInStub) {
flake8_pyi::rules::no_return_argument_annotation(checker, parameters); flake8_pyi::rules::no_return_argument_annotation(checker, parameters);
@ -194,12 +194,12 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::global_statement(checker, name); pylint::rules::global_statement(checker, name);
} }
if checker.enabled(Rule::LRUCacheWithoutParameters) { if checker.enabled(Rule::LRUCacheWithoutParameters) {
if checker.settings.target_version >= PythonVersion::Py38 { if checker.settings.target_version >= PythonVersion::PY38 {
pyupgrade::rules::lru_cache_without_parameters(checker, decorator_list); pyupgrade::rules::lru_cache_without_parameters(checker, decorator_list);
} }
} }
if checker.enabled(Rule::LRUCacheWithMaxsizeNone) { if checker.enabled(Rule::LRUCacheWithMaxsizeNone) {
if checker.settings.target_version >= PythonVersion::Py39 { if checker.settings.target_version >= PythonVersion::PY39 {
pyupgrade::rules::lru_cache_with_maxsize_none(checker, decorator_list); pyupgrade::rules::lru_cache_with_maxsize_none(checker, decorator_list);
} }
} }
@ -445,7 +445,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pyupgrade::rules::useless_object_inheritance(checker, class_def); pyupgrade::rules::useless_object_inheritance(checker, class_def);
} }
if checker.enabled(Rule::ReplaceStrEnum) { if checker.enabled(Rule::ReplaceStrEnum) {
if checker.settings.target_version >= PythonVersion::Py311 { if checker.settings.target_version >= PythonVersion::PY311 {
pyupgrade::rules::replace_str_enum(checker, class_def); pyupgrade::rules::replace_str_enum(checker, class_def);
} }
} }
@ -765,7 +765,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} }
} }
if checker.enabled(Rule::UnnecessaryFutureImport) { if checker.enabled(Rule::UnnecessaryFutureImport) {
if checker.settings.target_version >= PythonVersion::Py37 { if checker.settings.target_version >= PythonVersion::PY37 {
if let Some("__future__") = module { if let Some("__future__") = module {
pyupgrade::rules::unnecessary_future_import(checker, stmt, names); pyupgrade::rules::unnecessary_future_import(checker, stmt, names);
} }
@ -1039,7 +1039,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
} }
} }
if checker.enabled(Rule::TimeoutErrorAlias) { if checker.enabled(Rule::TimeoutErrorAlias) {
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
if let Some(item) = exc { if let Some(item) = exc {
pyupgrade::rules::timeout_error_alias_raise(checker, item); pyupgrade::rules::timeout_error_alias_raise(checker, item);
} }
@ -1431,7 +1431,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
flake8_bugbear::rules::jump_statement_in_finally(checker, finalbody); flake8_bugbear::rules::jump_statement_in_finally(checker, finalbody);
} }
if checker.enabled(Rule::ContinueInFinally) { if checker.enabled(Rule::ContinueInFinally) {
if checker.settings.target_version <= PythonVersion::Py38 { if checker.settings.target_version <= PythonVersion::PY38 {
pylint::rules::continue_in_finally(checker, finalbody); pylint::rules::continue_in_finally(checker, finalbody);
} }
} }
@ -1455,7 +1455,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pyupgrade::rules::os_error_alias_handlers(checker, handlers); pyupgrade::rules::os_error_alias_handlers(checker, handlers);
} }
if checker.enabled(Rule::TimeoutErrorAlias) { if checker.enabled(Rule::TimeoutErrorAlias) {
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
pyupgrade::rules::timeout_error_alias_handlers(checker, handlers); pyupgrade::rules::timeout_error_alias_handlers(checker, handlers);
} }
} }

View file

@ -2115,7 +2115,7 @@ impl<'a> Checker<'a> {
}; };
let standard_builtins = python_builtins( let standard_builtins = python_builtins(
self.settings.target_version.minor(), self.settings.target_version.minor,
self.source_type.is_ipynb(), self.source_type.is_ipynb(),
); );
for builtin in standard_builtins { for builtin in standard_builtins {

View file

@ -399,7 +399,7 @@ impl ShadowedKind {
if is_python_builtin( if is_python_builtin(
new_name, new_name,
checker.settings.target_version.minor(), checker.settings.target_version.minor,
checker.source_type.is_ipynb(), checker.source_type.is_ipynb(),
) { ) {
return ShadowedKind::BuiltIn; return ShadowedKind::BuiltIn;

View file

@ -36,7 +36,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("fastapi").join(path).as_path(), Path::new("fastapi").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
target_version: settings::types::PythonVersion::Py38, target_version: ruff_python_ast::PythonVersion::PY38,
..settings::LinterSettings::for_rule(rule_code) ..settings::LinterSettings::for_rule(rule_code)
}, },
)?; )?;

View file

@ -8,7 +8,7 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::importer::ImportRequest; use crate::importer::ImportRequest;
use crate::rules::fastapi::rules::is_fastapi_route; use crate::rules::fastapi::rules::is_fastapi_route;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Identifies FastAPI routes with deprecated uses of `Depends` or similar. /// Identifies FastAPI routes with deprecated uses of `Depends` or similar.
@ -77,7 +77,7 @@ impl Violation for FastApiNonAnnotatedDependency {
} }
fn fix_title(&self) -> Option<String> { fn fix_title(&self) -> Option<String> {
let title = if self.py_version >= PythonVersion::Py39 { let title = if self.py_version >= PythonVersion::PY39 {
"Replace with `typing.Annotated`" "Replace with `typing.Annotated`"
} else { } else {
"Replace with `typing_extensions.Annotated`" "Replace with `typing_extensions.Annotated`"
@ -232,7 +232,7 @@ fn create_diagnostic(
); );
let try_generate_fix = || { let try_generate_fix = || {
let module = if checker.settings.target_version >= PythonVersion::Py39 { let module = if checker.settings.target_version >= PythonVersion::PY39 {
"typing" "typing"
} else { } else {
"typing_extensions" "typing_extensions"

View file

@ -15,7 +15,7 @@ use ruff_python_semantic::{Definition, SemanticModel};
use ruff_text_size::{TextRange, TextSize}; use ruff_text_size::{TextRange, TextSize};
use crate::importer::{ImportRequest, Importer}; use crate::importer::{ImportRequest, Importer};
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// Return the name of the function, if it's overloaded. /// Return the name of the function, if it's overloaded.
pub(crate) fn overloaded_name<'a>( pub(crate) fn overloaded_name<'a>(
@ -130,7 +130,7 @@ impl AutoPythonType {
.get_or_import_symbol( .get_or_import_symbol(
&ImportRequest::import_from( &ImportRequest::import_from(
"typing", "typing",
if target_version >= PythonVersion::Py311 { if target_version >= PythonVersion::PY311 {
"Never" "Never"
} else { } else {
"NoReturn" "NoReturn"
@ -152,7 +152,7 @@ impl AutoPythonType {
Some((expr, vec![])) Some((expr, vec![]))
} }
AutoPythonType::Union(python_types) => { AutoPythonType::Union(python_types) => {
if target_version >= PythonVersion::Py310 { if target_version >= PythonVersion::PY310 {
// Aggregate all the individual types (e.g., `int`, `float`). // Aggregate all the individual types (e.g., `int`, `float`).
let names = python_types let names = python_types
.iter() .iter()

View file

@ -11,9 +11,9 @@ mod tests {
use crate::assert_messages; use crate::assert_messages;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::PythonVersion;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::test_path; use crate::test::test_path;
use ruff_python_ast::PythonVersion;
#[test] #[test]
fn defaults() -> Result<()> { fn defaults() -> Result<()> {
@ -128,7 +128,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_annotations/auto_return_type.py"), Path::new("flake8_annotations/auto_return_type.py"),
&LinterSettings { &LinterSettings {
target_version: PythonVersion::Py38, target_version: PythonVersion::PY38,
..LinterSettings::for_rules(vec![ ..LinterSettings::for_rules(vec![
Rule::MissingReturnTypeUndocumentedPublicFunction, Rule::MissingReturnTypeUndocumentedPublicFunction,
Rule::MissingReturnTypePrivateFunction, Rule::MissingReturnTypePrivateFunction,

View file

@ -523,7 +523,7 @@ fn check_dynamically_typed<F>(
if type_hint_resolves_to_any( if type_hint_resolves_to_any(
parsed_annotation.expression(), parsed_annotation.expression(),
checker, checker,
checker.settings.target_version.minor(), checker.settings.target_version.minor,
) { ) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
AnyType { name: func() }, AnyType { name: func() },
@ -532,7 +532,7 @@ fn check_dynamically_typed<F>(
} }
} }
} else { } else {
if type_hint_resolves_to_any(annotation, checker, checker.settings.target_version.minor()) { if type_hint_resolves_to_any(annotation, checker, checker.settings.target_version.minor) {
diagnostics.push(Diagnostic::new( diagnostics.push(Diagnostic::new(
AnyType { name: func() }, AnyType { name: func() },
annotation.range(), annotation.range(),

View file

@ -11,9 +11,9 @@ mod tests {
use crate::assert_messages; use crate::assert_messages;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::PythonVersion;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::test_path; use crate::test::test_path;
use ruff_python_ast::PythonVersion;
#[test_case(Rule::CancelScopeNoCheckpoint, Path::new("ASYNC100.py"))] #[test_case(Rule::CancelScopeNoCheckpoint, Path::new("ASYNC100.py"))]
#[test_case(Rule::TrioSyncCall, Path::new("ASYNC105.py"))] #[test_case(Rule::TrioSyncCall, Path::new("ASYNC105.py"))]
@ -44,7 +44,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_async").join(path), Path::new("flake8_async").join(path),
&LinterSettings { &LinterSettings {
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..LinterSettings::for_rule(Rule::AsyncFunctionWithTimeout) ..LinterSettings::for_rule(Rule::AsyncFunctionWithTimeout)
}, },
)?; )?;

View file

@ -6,7 +6,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_async::helpers::AsyncModule; use crate::rules::flake8_async::helpers::AsyncModule;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
#[allow(clippy::doc_link_with_quotes)] #[allow(clippy::doc_link_with_quotes)]
/// ## What it does /// ## What it does
@ -108,7 +108,7 @@ pub(crate) fn async_function_with_timeout(checker: &Checker, function_def: &ast:
}; };
// asyncio.timeout feature was first introduced in Python 3.11 // asyncio.timeout feature was first introduced in Python 3.11
if module == AsyncModule::AsyncIo && checker.settings.target_version < PythonVersion::Py311 { if module == AsyncModule::AsyncIo && checker.settings.target_version < PythonVersion::PY311 {
return; return;
} }

View file

@ -16,7 +16,7 @@ mod tests {
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::test_path; use crate::test::test_path;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
#[test_case(Rule::AbstractBaseClassWithoutAbstractMethod, Path::new("B024.py"))] #[test_case(Rule::AbstractBaseClassWithoutAbstractMethod, Path::new("B024.py"))]
#[test_case(Rule::AssertFalse, Path::new("B011.py"))] #[test_case(Rule::AssertFalse, Path::new("B011.py"))]
@ -83,7 +83,7 @@ mod tests {
#[test_case( #[test_case(
Rule::ClassAsDataStructure, Rule::ClassAsDataStructure,
Path::new("class_as_data_structure.py"), Path::new("class_as_data_structure.py"),
PythonVersion::Py39 PythonVersion::PY39
)] )]
fn rules_with_target_version( fn rules_with_target_version(
rule_code: Rule, rule_code: Rule,
@ -93,8 +93,8 @@ mod tests {
let snapshot = format!( let snapshot = format!(
"{}_py{}{}_{}", "{}_py{}{}_{}",
rule_code.noqa_code(), rule_code.noqa_code(),
target_version.major(), target_version.major,
target_version.minor(), target_version.minor,
path.to_string_lossy(), path.to_string_lossy(),
); );
let diagnostics = test_path( let diagnostics = test_path(

View file

@ -1,9 +1,9 @@
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_bugbear::rules::is_infinite_iterable; use crate::rules::flake8_bugbear::rules::is_infinite_iterable;
use crate::settings::types::PythonVersion;
use ruff_diagnostics::{Diagnostic, FixAvailability, Violation}; use ruff_diagnostics::{Diagnostic, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::ExprCall; use ruff_python_ast::ExprCall;
use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for `itertools.batched` calls without an explicit `strict` parameter. /// Checks for `itertools.batched` calls without an explicit `strict` parameter.
@ -59,7 +59,7 @@ impl Violation for BatchedWithoutExplicitStrict {
/// B911 /// B911
pub(crate) fn batched_without_explicit_strict(checker: &Checker, call: &ExprCall) { pub(crate) fn batched_without_explicit_strict(checker: &Checker, call: &ExprCall) {
if checker.settings.target_version < PythonVersion::Py313 { if checker.settings.target_version < PythonVersion::PY313 {
return; return;
} }

View file

@ -5,7 +5,7 @@ use ruff_python_semantic::analyze::visibility::{self, Visibility::Public};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for classes that only have a public `__init__` method, /// Checks for classes that only have a public `__init__` method,
@ -78,7 +78,7 @@ pub(crate) fn class_as_data_structure(checker: &Checker, class_def: &ast::StmtCl
// skip `self` // skip `self`
.skip(1) .skip(1)
.all(|param| param.annotation().is_some() && !param.is_variadic()) .all(|param| param.annotation().is_some() && !param.is_variadic())
&& (func_def.parameters.kwonlyargs.is_empty() || checker.settings.target_version >= PythonVersion::Py310) && (func_def.parameters.kwonlyargs.is_empty() || checker.settings.target_version >= PythonVersion::PY310)
// `__init__` should not have complicated logic in it // `__init__` should not have complicated logic in it
// only assignments // only assignments
&& func_def && func_def

View file

@ -1,5 +1,5 @@
use crate::settings::types::PythonVersion;
use ruff_python_ast::PySourceType; use ruff_python_ast::PySourceType;
use ruff_python_ast::PythonVersion;
use ruff_python_stdlib::builtins::is_python_builtin; use ruff_python_stdlib::builtins::is_python_builtin;
pub(super) fn shadows_builtin( pub(super) fn shadows_builtin(
@ -8,7 +8,7 @@ pub(super) fn shadows_builtin(
ignorelist: &[String], ignorelist: &[String],
python_version: PythonVersion, python_version: PythonVersion,
) -> bool { ) -> bool {
if is_python_builtin(name, python_version.minor(), source_type.is_ipynb()) { if is_python_builtin(name, python_version.minor, source_type.is_ipynb()) {
ignorelist.iter().all(|ignore| ignore != name) ignorelist.iter().all(|ignore| ignore != name)
} else { } else {
false false

View file

@ -13,9 +13,9 @@ mod tests {
use crate::assert_messages; use crate::assert_messages;
use crate::registry::Rule; use crate::registry::Rule;
use crate::rules::flake8_builtins; use crate::rules::flake8_builtins;
use crate::settings::types::PythonVersion;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::{test_path, test_resource_path}; use crate::test::{test_path, test_resource_path};
use ruff_python_ast::PythonVersion;
#[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))] #[test_case(Rule::BuiltinVariableShadowing, Path::new("A001.py"))]
#[test_case(Rule::BuiltinArgumentShadowing, Path::new("A002.py"))] #[test_case(Rule::BuiltinArgumentShadowing, Path::new("A002.py"))]
@ -217,7 +217,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_builtins").join(path).as_path(), Path::new("flake8_builtins").join(path).as_path(),
&LinterSettings { &LinterSettings {
target_version: PythonVersion::Py38, target_version: PythonVersion::PY38,
..LinterSettings::for_rule(rule_code) ..LinterSettings::for_rule(rule_code)
}, },
)?; )?;

View file

@ -145,5 +145,5 @@ fn is_allowed_module(settings: &LinterSettings, module: &str) -> bool {
return true; return true;
} }
!is_known_standard_library(settings.target_version.minor(), module) !is_known_standard_library(settings.target_version.minor, module)
} }

View file

@ -9,9 +9,9 @@ mod tests {
use test_case::test_case; use test_case::test_case;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::PythonVersion;
use crate::test::test_path; use crate::test::test_path;
use crate::{assert_messages, settings}; use crate::{assert_messages, settings};
use ruff_python_ast::PythonVersion;
#[test_case(Path::new("edge_case.py"))] #[test_case(Path::new("edge_case.py"))]
#[test_case(Path::new("from_typing_import.py"))] #[test_case(Path::new("from_typing_import.py"))]
@ -30,7 +30,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_future_annotations").join(path).as_path(), Path::new("flake8_future_annotations").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py37, target_version: PythonVersion::PY37,
..settings::LinterSettings::for_rule(Rule::FutureRewritableTypeAnnotation) ..settings::LinterSettings::for_rule(Rule::FutureRewritableTypeAnnotation)
}, },
)?; )?;
@ -49,7 +49,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_future_annotations").join(path).as_path(), Path::new("flake8_future_annotations").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py37, target_version: PythonVersion::PY37,
..settings::LinterSettings::for_rule(Rule::FutureRequiredTypeAnnotation) ..settings::LinterSettings::for_rule(Rule::FutureRequiredTypeAnnotation)
}, },
)?; )?;

View file

@ -6,11 +6,12 @@ mod tests {
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use ruff_python_ast::PythonVersion;
use test_case::test_case; use test_case::test_case;
use crate::registry::Rule; use crate::registry::Rule;
use crate::rules::pep8_naming; use crate::rules::pep8_naming;
use crate::settings::types::{PreviewMode, PythonVersion}; use crate::settings::types::PreviewMode;
use crate::test::test_path; use crate::test::test_path;
use crate::{assert_messages, settings}; use crate::{assert_messages, settings};
@ -188,7 +189,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_pyi").join(path).as_path(), Path::new("flake8_pyi").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py38, target_version: PythonVersion::PY38,
..settings::LinterSettings::for_rule(rule_code) ..settings::LinterSettings::for_rule(rule_code)
}, },
)?; )?;

View file

@ -12,7 +12,7 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::importer::{ImportRequest, ResolutionError}; use crate::importer::{ImportRequest, ResolutionError};
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for methods that use custom [`TypeVar`s][typing_TypeVar] in their /// Checks for methods that use custom [`TypeVar`s][typing_TypeVar] in their
@ -560,7 +560,7 @@ fn replace_custom_typevar_with_self(
/// This is because it was added to the `typing` module on Python 3.11, /// This is because it was added to the `typing` module on Python 3.11,
/// but is available from the backport package `typing_extensions` on all versions. /// but is available from the backport package `typing_extensions` on all versions.
fn import_self(checker: &Checker, position: TextSize) -> Result<(Edit, String), ResolutionError> { fn import_self(checker: &Checker, position: TextSize) -> Result<(Edit, String), ResolutionError> {
let source_module = if checker.settings.target_version >= PythonVersion::Py311 { let source_module = if checker.settings.target_version >= PythonVersion::PY311 {
"typing" "typing"
} else { } else {
"typing_extensions" "typing_extensions"

View file

@ -6,7 +6,7 @@ use ruff_python_ast as ast;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion::Py311; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for uses of `typing.NoReturn` (and `typing_extensions.NoReturn`) for /// Checks for uses of `typing.NoReturn` (and `typing_extensions.NoReturn`) for
@ -67,7 +67,7 @@ pub(crate) fn no_return_argument_annotation(checker: &Checker, parameters: &ast:
if is_no_return(annotation, checker) { if is_no_return(annotation, checker) {
checker.report_diagnostic(Diagnostic::new( checker.report_diagnostic(Diagnostic::new(
NoReturnArgumentAnnotationInStub { NoReturnArgumentAnnotationInStub {
module: if checker.settings.target_version >= Py311 { module: if checker.settings.target_version >= PythonVersion::PY311 {
TypingModule::Typing TypingModule::Typing
} else { } else {
TypingModule::TypingExtensions TypingModule::TypingExtensions

View file

@ -1,11 +1,11 @@
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::importer::ImportRequest; use crate::importer::ImportRequest;
use crate::settings::types::PythonVersion;
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::helpers::map_subscript; use ruff_python_ast::helpers::map_subscript;
use ruff_python_ast::identifier::Identifier; use ruff_python_ast::identifier::Identifier;
use ruff_python_ast::PythonVersion;
use ruff_python_semantic::analyze; use ruff_python_semantic::analyze;
use ruff_python_semantic::analyze::class::might_be_generic; use ruff_python_semantic::analyze::class::might_be_generic;
use ruff_python_semantic::analyze::visibility::{is_abstract, is_final, is_overload}; use ruff_python_semantic::analyze::visibility::{is_abstract, is_final, is_overload};
@ -215,7 +215,7 @@ fn replace_with_self_fix(
let semantic = checker.semantic(); let semantic = checker.semantic();
let (self_import, self_binding) = { let (self_import, self_binding) = {
let source_module = if checker.settings.target_version >= PythonVersion::Py311 { let source_module = if checker.settings.target_version >= PythonVersion::PY311 {
"typing" "typing"
} else { } else {
"typing_extensions" "typing_extensions"

View file

@ -5,7 +5,7 @@ use ruff_python_ast::{self as ast, ParameterWithDefault};
use ruff_python_semantic::analyze::function_type; use ruff_python_semantic::analyze::function_type;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for the presence of [PEP 484]-style positional-only parameters. /// Checks for the presence of [PEP 484]-style positional-only parameters.
@ -56,7 +56,7 @@ impl Violation for Pep484StylePositionalOnlyParameter {
/// PYI063 /// PYI063
pub(crate) fn pep_484_positional_parameter(checker: &Checker, function_def: &ast::StmtFunctionDef) { pub(crate) fn pep_484_positional_parameter(checker: &Checker, function_def: &ast::StmtFunctionDef) {
// PEP 570 was introduced in Python 3.8. // PEP 570 was introduced in Python 3.8.
if checker.settings.target_version < PythonVersion::Py38 { if checker.settings.target_version < PythonVersion::PY38 {
return; return;
} }

View file

@ -5,14 +5,14 @@ use ruff_python_ast::{
self as ast, self as ast,
helpers::{pep_604_union, typing_optional}, helpers::{pep_604_union, typing_optional},
name::Name, name::Name,
Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, PythonVersion,
}; };
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union}; use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{checkers::ast::Checker, importer::ImportRequest, settings::types::PythonVersion}; use crate::{checkers::ast::Checker, importer::ImportRequest};
/// ## What it does /// ## What it does
/// Checks for redundant `Literal[None]` annotations. /// Checks for redundant `Literal[None]` annotations.
@ -112,7 +112,7 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex
let union_kind = if literal_elements.is_empty() { let union_kind = if literal_elements.is_empty() {
UnionKind::NoUnion UnionKind::NoUnion
} else if (checker.settings.target_version >= PythonVersion::Py310) } else if (checker.settings.target_version >= PythonVersion::PY310)
|| checker.source_type.is_stub() || checker.source_type.is_stub()
{ {
UnionKind::BitOr UnionKind::BitOr

View file

@ -8,8 +8,8 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::importer::ImportRequest; use crate::importer::ImportRequest;
use crate::rules::flake8_pyi::rules::TypingModule; use crate::rules::flake8_pyi::rules::TypingModule;
use crate::settings::types::PythonVersion;
use crate::Locator; use crate::Locator;
use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for typed function arguments in stubs with complex default values. /// Checks for typed function arguments in stubs with complex default values.
@ -667,7 +667,7 @@ pub(crate) fn type_alias_without_annotation(checker: &Checker, value: &Expr, tar
return; return;
} }
let module = if checker.settings.target_version >= PythonVersion::Py310 { let module = if checker.settings.target_version >= PythonVersion::PY310 {
TypingModule::Typing TypingModule::Typing
} else { } else {
TypingModule::TypingExtensions TypingModule::TypingExtensions

View file

@ -12,9 +12,9 @@ mod tests {
use crate::assert_messages; use crate::assert_messages;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::PythonVersion;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::test_path; use crate::test::test_path;
use ruff_python_ast::PythonVersion;
use super::settings::Quote; use super::settings::Quote;
@ -62,7 +62,7 @@ mod tests {
avoid_escape: true, avoid_escape: true,
}, },
..LinterSettings::for_rule(Rule::AvoidableEscapedQuote) ..LinterSettings::for_rule(Rule::AvoidableEscapedQuote)
.with_target_version(PythonVersion::Py311) .with_target_version(PythonVersion::PY311)
}, },
)?; )?;
assert_messages!(diagnostics); assert_messages!(diagnostics);
@ -81,7 +81,7 @@ mod tests {
avoid_escape: true, avoid_escape: true,
}, },
..LinterSettings::for_rule(Rule::AvoidableEscapedQuote) ..LinterSettings::for_rule(Rule::AvoidableEscapedQuote)
.with_target_version(PythonVersion::Py311) .with_target_version(PythonVersion::PY311)
}, },
)?; )?;
assert_messages!(diagnostics); assert_messages!(diagnostics);

View file

@ -92,7 +92,7 @@ impl<'a> AvoidableEscapedQuoteChecker<'a> {
Self { Self {
locator, locator,
quotes_settings: &settings.flake8_quotes, quotes_settings: &settings.flake8_quotes,
supports_pep701: settings.target_version.supports_pep701(), supports_pep701: settings.target_version.supports_pep_701(),
diagnostics: vec![], diagnostics: vec![],
} }
} }

View file

@ -10,6 +10,7 @@ mod tests {
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use ruff_python_ast::PythonVersion;
use test_case::test_case; use test_case::test_case;
use crate::registry::{Linter, Rule}; use crate::registry::{Linter, Rule};
@ -91,7 +92,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("flake8_type_checking").join(path).as_path(), Path::new("flake8_type_checking").join(path).as_path(),
&settings::LinterSettings { &settings::LinterSettings {
target_version: settings::types::PythonVersion::Py39, target_version: PythonVersion::PY39,
..settings::LinterSettings::for_rule(rule_code) ..settings::LinterSettings::for_rule(rule_code)
}, },
)?; )?;

View file

@ -10,8 +10,8 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::Rule; use crate::registry::Rule;
use crate::rules::flake8_type_checking::helpers::quote_type_expression; use crate::rules::flake8_type_checking::helpers::quote_type_expression;
use crate::settings::types::PythonVersion;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks if [PEP 613] explicit type aliases contain references to /// Checks if [PEP 613] explicit type aliases contain references to
@ -313,7 +313,7 @@ fn quotes_are_unremovable(
}) => { }) => {
match op { match op {
Operator::BitOr => { Operator::BitOr => {
if settings.target_version < PythonVersion::Py310 { if settings.target_version < PythonVersion::PY310 {
return true; return true;
} }
quotes_are_unremovable(semantic, left, settings) quotes_are_unremovable(semantic, left, settings)

View file

@ -15,7 +15,7 @@ use crate::rules::flake8_use_pathlib::violations::{
OsPathIsfile, OsPathIslink, OsPathJoin, OsPathSamefile, OsPathSplitext, OsReadlink, OsRemove, OsPathIsfile, OsPathIslink, OsPathJoin, OsPathSamefile, OsPathSplitext, OsReadlink, OsRemove,
OsRename, OsReplace, OsRmdir, OsStat, OsUnlink, PyPath, OsRename, OsReplace, OsRmdir, OsStat, OsUnlink, PyPath,
}; };
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) { pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
if let Some(diagnostic_kind) = checker if let Some(diagnostic_kind) = checker
@ -152,7 +152,7 @@ pub(crate) fn replaceable_by_pathlib(checker: &Checker, call: &ExprCall) {
), ),
// PTH115 // PTH115
// Python 3.9+ // Python 3.9+
["os", "readlink"] if checker.settings.target_version >= PythonVersion::Py39 => { ["os", "readlink"] if checker.settings.target_version >= PythonVersion::PY39 => {
Some(OsReadlink.into()) Some(OsReadlink.into())
} }
// PTH208, // PTH208,

View file

@ -9,9 +9,9 @@ use serde::{Deserialize, Serialize};
use strum_macros::EnumIter; use strum_macros::EnumIter;
use crate::package::PackageRoot; use crate::package::PackageRoot;
use crate::settings::types::PythonVersion;
use crate::warn_user_once; use crate::warn_user_once;
use ruff_macros::CacheKey; use ruff_macros::CacheKey;
use ruff_python_ast::PythonVersion;
use ruff_python_stdlib::sys::is_known_standard_library; use ruff_python_stdlib::sys::is_known_standard_library;
use super::types::{ImportBlock, Importable}; use super::types::{ImportBlock, Importable};
@ -117,7 +117,7 @@ pub(crate) fn categorize<'a>(
) )
} else if let Some((import_type, reason)) = known_modules.categorize(module_name) { } else if let Some((import_type, reason)) = known_modules.categorize(module_name) {
(import_type, reason) (import_type, reason)
} else if is_known_standard_library(target_version.minor(), module_base) { } else if is_known_standard_library(target_version.minor, module_base) {
( (
&ImportSection::Known(ImportType::StandardLibrary), &ImportSection::Known(ImportType::StandardLibrary),
Reason::KnownStandardLibrary, Reason::KnownStandardLibrary,

View file

@ -19,8 +19,8 @@ use types::{AliasData, ImportBlock, TrailingComma};
use crate::line_width::{LineLength, LineWidthBuilder}; use crate::line_width::{LineLength, LineWidthBuilder};
use crate::package::PackageRoot; use crate::package::PackageRoot;
use crate::settings::types::PythonVersion;
use crate::Locator; use crate::Locator;
use ruff_python_ast::PythonVersion;
mod annotate; mod annotate;
pub(crate) mod block; pub(crate) mod block;

View file

@ -6,11 +6,12 @@ mod tests {
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use ruff_python_ast::PythonVersion;
use test_case::test_case; use test_case::test_case;
use crate::assert_messages; use crate::assert_messages;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::{PreviewMode, PythonVersion}; use crate::settings::types::PreviewMode;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::test_path; use crate::test::test_path;
@ -24,7 +25,7 @@ mod tests {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path( let diagnostics = test_path(
Path::new("perflint").join(path).as_path(), Path::new("perflint").join(path).as_path(),
&LinterSettings::for_rule(rule_code).with_target_version(PythonVersion::Py310), &LinterSettings::for_rule(rule_code).with_target_version(PythonVersion::PY310),
)?; )?;
assert_messages!(snapshot, diagnostics); assert_messages!(snapshot, diagnostics);
Ok(()) Ok(())
@ -42,7 +43,7 @@ mod tests {
Path::new("perflint").join(path).as_path(), Path::new("perflint").join(path).as_path(),
&LinterSettings { &LinterSettings {
preview: PreviewMode::Enabled, preview: PreviewMode::Enabled,
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..LinterSettings::for_rule(rule_code) ..LinterSettings::for_rule(rule_code)
}, },
)?; )?;

View file

@ -5,7 +5,7 @@ use ruff_python_ast::{self as ast, Stmt};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for uses of except handling via `try`-`except` within `for` and /// Checks for uses of except handling via `try`-`except` within `for` and
@ -89,7 +89,7 @@ impl Violation for TryExceptInLoop {
/// PERF203 /// PERF203
pub(crate) fn try_except_in_loop(checker: &Checker, body: &[Stmt]) { pub(crate) fn try_except_in_loop(checker: &Checker, body: &[Stmt]) {
if checker.settings.target_version >= PythonVersion::Py311 { if checker.settings.target_version >= PythonVersion::PY311 {
return; return;
} }

View file

@ -218,7 +218,7 @@ mod tests {
let diagnostics = test_snippet( let diagnostics = test_snippet(
"PythonFinalizationError", "PythonFinalizationError",
&LinterSettings { &LinterSettings {
target_version: crate::settings::types::PythonVersion::Py312, target_version: ruff_python_ast::PythonVersion::PY312,
..LinterSettings::for_rule(Rule::UndefinedName) ..LinterSettings::for_rule(Rule::UndefinedName)
}, },
); );

View file

@ -9,13 +9,14 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use regex::Regex; use regex::Regex;
use ruff_python_ast::PythonVersion;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use test_case::test_case; use test_case::test_case;
use crate::registry::Rule; use crate::registry::Rule;
use crate::rules::{flake8_tidy_imports, pylint}; use crate::rules::{flake8_tidy_imports, pylint};
use crate::settings::types::{PreviewMode, PythonVersion}; use crate::settings::types::PreviewMode;
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::test_path; use crate::test::test_path;
use crate::{assert_messages, settings}; use crate::{assert_messages, settings};
@ -249,7 +250,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pylint/continue_in_finally.py"), Path::new("pylint/continue_in_finally.py"),
&LinterSettings::for_rule(Rule::ContinueInFinally) &LinterSettings::for_rule(Rule::ContinueInFinally)
.with_target_version(PythonVersion::Py37), .with_target_version(PythonVersion::PY37),
)?; )?;
assert_messages!(diagnostics); assert_messages!(diagnostics);
Ok(()) Ok(())

View file

@ -10,7 +10,7 @@ use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks duplicate characters in `str.strip` calls. /// Checks duplicate characters in `str.strip` calls.
@ -211,7 +211,7 @@ pub(crate) fn bad_str_strip_call(checker: &Checker, call: &ast::ExprCall) {
return; return;
} }
let removal = if checker.settings.target_version >= PythonVersion::Py39 { let removal = if checker.settings.target_version >= PythonVersion::PY39 {
RemovalKind::for_strip(strip) RemovalKind::for_strip(strip)
} else { } else {
None None

View file

@ -6,7 +6,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::pylint::helpers::is_known_dunder_method; use crate::rules::pylint::helpers::is_known_dunder_method;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for explicit use of dunder methods, like `__str__` and `__add__`. /// Checks for explicit use of dunder methods, like `__str__` and `__add__`.
@ -248,7 +248,7 @@ fn allowed_dunder_constants(dunder_method: &str, target_version: PythonVersion)
return true; return true;
} }
if target_version < PythonVersion::Py310 && matches!(dunder_method, "__aiter__" | "__anext__") { if target_version < PythonVersion::PY310 && matches!(dunder_method, "__aiter__" | "__anext__") {
return true; return true;
} }

View file

@ -6,7 +6,7 @@ use ruff_python_stdlib::builtins;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for an exception that is not raised. /// Checks for an exception that is not raised.
@ -75,6 +75,6 @@ fn is_builtin_exception(
.resolve_qualified_name(expr) .resolve_qualified_name(expr)
.is_some_and(|qualified_name| { .is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["" | "builtins", name] matches!(qualified_name.segments(), ["" | "builtins", name]
if builtins::is_exception(name, target_version.minor())) if builtins::is_exception(name, target_version.minor))
}) })
} }

View file

@ -10,11 +10,12 @@ mod tests {
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use ruff_python_ast::PythonVersion;
use test_case::test_case; use test_case::test_case;
use crate::registry::Rule; use crate::registry::Rule;
use crate::rules::pyupgrade; use crate::rules::pyupgrade;
use crate::settings::types::{PreviewMode, PythonVersion}; use crate::settings::types::PreviewMode;
use crate::test::test_path; use crate::test::test_path;
use crate::{assert_messages, settings}; use crate::{assert_messages, settings};
@ -155,7 +156,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/UP041.py"), Path::new("pyupgrade/UP041.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..settings::LinterSettings::for_rule(Rule::TimeoutErrorAlias) ..settings::LinterSettings::for_rule(Rule::TimeoutErrorAlias)
}, },
)?; )?;
@ -168,7 +169,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/UP040.py"), Path::new("pyupgrade/UP040.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py311, target_version: PythonVersion::PY311,
..settings::LinterSettings::for_rule(Rule::NonPEP695TypeAlias) ..settings::LinterSettings::for_rule(Rule::NonPEP695TypeAlias)
}, },
)?; )?;
@ -184,7 +185,7 @@ mod tests {
pyupgrade: pyupgrade::settings::Settings { pyupgrade: pyupgrade::settings::Settings {
keep_runtime_typing: true, keep_runtime_typing: true,
}, },
target_version: PythonVersion::Py37, target_version: PythonVersion::PY37,
..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation) ..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation)
}, },
)?; )?;
@ -200,7 +201,7 @@ mod tests {
pyupgrade: pyupgrade::settings::Settings { pyupgrade: pyupgrade::settings::Settings {
keep_runtime_typing: true, keep_runtime_typing: true,
}, },
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation) ..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation)
}, },
)?; )?;
@ -213,7 +214,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py37, target_version: PythonVersion::PY37,
..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation) ..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation)
}, },
)?; )?;
@ -226,7 +227,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation) ..settings::LinterSettings::for_rule(Rule::NonPEP585Annotation)
}, },
)?; )?;
@ -239,7 +240,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py37, target_version: PythonVersion::PY37,
..settings::LinterSettings::for_rules([ ..settings::LinterSettings::for_rules([
Rule::NonPEP604AnnotationUnion, Rule::NonPEP604AnnotationUnion,
Rule::NonPEP604AnnotationOptional, Rule::NonPEP604AnnotationOptional,
@ -255,7 +256,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/future_annotations.py"), Path::new("pyupgrade/future_annotations.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..settings::LinterSettings::for_rules([ ..settings::LinterSettings::for_rules([
Rule::NonPEP604AnnotationUnion, Rule::NonPEP604AnnotationUnion,
Rule::NonPEP604AnnotationOptional, Rule::NonPEP604AnnotationOptional,
@ -271,7 +272,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("pyupgrade/UP017.py"), Path::new("pyupgrade/UP017.py"),
&settings::LinterSettings { &settings::LinterSettings {
target_version: PythonVersion::Py311, target_version: PythonVersion::PY311,
..settings::LinterSettings::for_rule(Rule::DatetimeTimezoneUTC) ..settings::LinterSettings::for_rule(Rule::DatetimeTimezoneUTC)
}, },
)?; )?;
@ -285,7 +286,7 @@ mod tests {
Path::new("pyupgrade/UP044.py"), Path::new("pyupgrade/UP044.py"),
&settings::LinterSettings { &settings::LinterSettings {
preview: PreviewMode::Enabled, preview: PreviewMode::Enabled,
target_version: PythonVersion::Py311, target_version: PythonVersion::PY311,
..settings::LinterSettings::for_rule(Rule::NonPEP646Unpack) ..settings::LinterSettings::for_rule(Rule::NonPEP646Unpack)
}, },
)?; )?;

View file

@ -10,8 +10,8 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::pyupgrade::fixes; use crate::rules::pyupgrade::fixes;
use crate::settings::types::PythonVersion;
use crate::Locator; use crate::Locator;
use ruff_python_ast::PythonVersion;
/// An import was moved and renamed as part of a deprecation. /// An import was moved and renamed as part of a deprecation.
/// For example, `typing.AbstractSet` was moved to `collections.abc.Set`. /// For example, `typing.AbstractSet` was moved to `collections.abc.Set`.
@ -432,7 +432,7 @@ impl<'a> ImportReplacer<'a> {
fn with_renames(&self) -> Vec<WithRename> { fn with_renames(&self) -> Vec<WithRename> {
let mut operations = vec![]; let mut operations = vec![];
if self.module == "typing" { if self.module == "typing" {
if self.version >= PythonVersion::Py39 { if self.version >= PythonVersion::PY39 {
for member in &self.import_from_stmt.names { for member in &self.import_from_stmt.names {
if let Some(target) = TYPING_TO_RENAME_PY39.iter().find_map(|(name, target)| { if let Some(target) = TYPING_TO_RENAME_PY39.iter().find_map(|(name, target)| {
if &member.name == *name { if &member.name == *name {
@ -470,7 +470,7 @@ impl<'a> ImportReplacer<'a> {
"typing_extensions" => { "typing_extensions" => {
// `typing_extensions` to `collections.abc` // `typing_extensions` to `collections.abc`
let mut typing_extensions_to_collections_abc = vec![]; let mut typing_extensions_to_collections_abc = vec![];
if self.version >= PythonVersion::Py312 { if self.version >= PythonVersion::PY312 {
typing_extensions_to_collections_abc typing_extensions_to_collections_abc
.extend(TYPING_EXTENSIONS_TO_COLLECTIONS_ABC_312); .extend(TYPING_EXTENSIONS_TO_COLLECTIONS_ABC_312);
} }
@ -482,7 +482,7 @@ impl<'a> ImportReplacer<'a> {
// `typing_extensions` to `warnings` // `typing_extensions` to `warnings`
let mut typing_extensions_to_warnings = vec![]; let mut typing_extensions_to_warnings = vec![];
if self.version >= PythonVersion::Py313 { if self.version >= PythonVersion::PY313 {
typing_extensions_to_warnings.extend(TYPING_EXTENSIONS_TO_WARNINGS_313); typing_extensions_to_warnings.extend(TYPING_EXTENSIONS_TO_WARNINGS_313);
} }
if let Some(operation) = if let Some(operation) =
@ -493,10 +493,10 @@ impl<'a> ImportReplacer<'a> {
// `typing_extensions` to `types` // `typing_extensions` to `types`
let mut typing_extensions_to_types = vec![]; let mut typing_extensions_to_types = vec![];
if self.version >= PythonVersion::Py312 { if self.version >= PythonVersion::PY312 {
typing_extensions_to_types.extend(TYPING_EXTENSIONS_TO_TYPES_312); typing_extensions_to_types.extend(TYPING_EXTENSIONS_TO_TYPES_312);
} }
if self.version >= PythonVersion::Py313 { if self.version >= PythonVersion::PY313 {
typing_extensions_to_types.extend(TYPING_EXTENSIONS_TO_TYPES_313); typing_extensions_to_types.extend(TYPING_EXTENSIONS_TO_TYPES_313);
} }
if let Some(operation) = self.try_replace(&typing_extensions_to_types, "types") { if let Some(operation) = self.try_replace(&typing_extensions_to_types, "types") {
@ -505,25 +505,25 @@ impl<'a> ImportReplacer<'a> {
// `typing_extensions` to `typing` // `typing_extensions` to `typing`
let mut typing_extensions_to_typing = TYPING_EXTENSIONS_TO_TYPING.to_vec(); let mut typing_extensions_to_typing = TYPING_EXTENSIONS_TO_TYPING.to_vec();
if self.version >= PythonVersion::Py37 { if self.version >= PythonVersion::PY37 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_37); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_37);
} }
if self.version >= PythonVersion::Py38 { if self.version >= PythonVersion::PY38 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_38); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_38);
} }
if self.version >= PythonVersion::Py39 { if self.version >= PythonVersion::PY39 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_39); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_39);
} }
if self.version >= PythonVersion::Py310 { if self.version >= PythonVersion::PY310 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_310); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_310);
} }
if self.version >= PythonVersion::Py311 { if self.version >= PythonVersion::PY311 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_311); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_311);
} }
if self.version >= PythonVersion::Py312 { if self.version >= PythonVersion::PY312 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_312); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_312);
} }
if self.version >= PythonVersion::Py313 { if self.version >= PythonVersion::PY313 {
typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_313); typing_extensions_to_typing.extend(TYPING_EXTENSIONS_TO_TYPING_313);
} }
if let Some(operation) = self.try_replace(&typing_extensions_to_typing, "typing") { if let Some(operation) = self.try_replace(&typing_extensions_to_typing, "typing") {
@ -532,10 +532,10 @@ impl<'a> ImportReplacer<'a> {
} }
"mypy_extensions" => { "mypy_extensions" => {
let mut mypy_extensions_to_typing = vec![]; let mut mypy_extensions_to_typing = vec![];
if self.version >= PythonVersion::Py37 { if self.version >= PythonVersion::PY37 {
mypy_extensions_to_typing.extend(MYPY_EXTENSIONS_TO_TYPING_37); mypy_extensions_to_typing.extend(MYPY_EXTENSIONS_TO_TYPING_37);
} }
if self.version >= PythonVersion::Py38 { if self.version >= PythonVersion::PY38 {
mypy_extensions_to_typing.extend(MYPY_EXTENSIONS_TO_TYPING_38); mypy_extensions_to_typing.extend(MYPY_EXTENSIONS_TO_TYPING_38);
} }
if let Some(operation) = self.try_replace(&mypy_extensions_to_typing, "typing") { if let Some(operation) = self.try_replace(&mypy_extensions_to_typing, "typing") {
@ -545,10 +545,10 @@ impl<'a> ImportReplacer<'a> {
"typing" => { "typing" => {
// `typing` to `collections.abc` // `typing` to `collections.abc`
let mut typing_to_collections_abc = vec![]; let mut typing_to_collections_abc = vec![];
if self.version >= PythonVersion::Py39 { if self.version >= PythonVersion::PY39 {
typing_to_collections_abc.extend(TYPING_TO_COLLECTIONS_ABC_39); typing_to_collections_abc.extend(TYPING_TO_COLLECTIONS_ABC_39);
} }
if self.version >= PythonVersion::Py310 { if self.version >= PythonVersion::PY310 {
typing_to_collections_abc.extend(TYPING_TO_COLLECTIONS_ABC_310); typing_to_collections_abc.extend(TYPING_TO_COLLECTIONS_ABC_310);
} }
if let Some(operation) = if let Some(operation) =
@ -559,7 +559,7 @@ impl<'a> ImportReplacer<'a> {
// `typing` to `collections` // `typing` to `collections`
let mut typing_to_collections = vec![]; let mut typing_to_collections = vec![];
if self.version >= PythonVersion::Py39 { if self.version >= PythonVersion::PY39 {
typing_to_collections.extend(TYPING_TO_COLLECTIONS_39); typing_to_collections.extend(TYPING_TO_COLLECTIONS_39);
} }
if let Some(operation) = self.try_replace(&typing_to_collections, "collections") { if let Some(operation) = self.try_replace(&typing_to_collections, "collections") {
@ -568,19 +568,19 @@ impl<'a> ImportReplacer<'a> {
// `typing` to `re` // `typing` to `re`
let mut typing_to_re = vec![]; let mut typing_to_re = vec![];
if self.version >= PythonVersion::Py39 { if self.version >= PythonVersion::PY39 {
typing_to_re.extend(TYPING_TO_RE_39); typing_to_re.extend(TYPING_TO_RE_39);
} }
if let Some(operation) = self.try_replace(&typing_to_re, "re") { if let Some(operation) = self.try_replace(&typing_to_re, "re") {
operations.push(operation); operations.push(operation);
} }
} }
"typing.re" if self.version >= PythonVersion::Py39 => { "typing.re" if self.version >= PythonVersion::PY39 => {
if let Some(operation) = self.try_replace(TYPING_RE_TO_RE_39, "re") { if let Some(operation) = self.try_replace(TYPING_RE_TO_RE_39, "re") {
operations.push(operation); operations.push(operation);
} }
} }
"backports.strenum" if self.version >= PythonVersion::Py311 => { "backports.strenum" if self.version >= PythonVersion::PY311 => {
if let Some(operation) = self.try_replace(BACKPORTS_STR_ENUM_TO_ENUM_311, "enum") { if let Some(operation) = self.try_replace(BACKPORTS_STR_ENUM_TO_ENUM_311, "enum") {
operations.push(operation); operations.push(operation);
} }

View file

@ -13,7 +13,7 @@ use ruff_text_size::{Ranged, TextLen, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::edits::{adjust_indentation, delete_stmt}; use crate::fix::edits::{adjust_indentation, delete_stmt};
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for conditional blocks gated on `sys.version_info` comparisons /// Checks for conditional blocks gated on `sys.version_info` comparisons
@ -456,17 +456,17 @@ mod tests {
use super::*; use super::*;
#[test_case(PythonVersion::Py37, & [2], true, true; "compare-2.0")] #[test_case(PythonVersion::PY37, & [2], true, true; "compare-2.0")]
#[test_case(PythonVersion::Py37, & [2, 0], true, true; "compare-2.0-whole")] #[test_case(PythonVersion::PY37, & [2, 0], true, true; "compare-2.0-whole")]
#[test_case(PythonVersion::Py37, & [3], true, true; "compare-3.0")] #[test_case(PythonVersion::PY37, & [3], true, true; "compare-3.0")]
#[test_case(PythonVersion::Py37, & [3, 0], true, true; "compare-3.0-whole")] #[test_case(PythonVersion::PY37, & [3, 0], true, true; "compare-3.0-whole")]
#[test_case(PythonVersion::Py37, & [3, 1], true, true; "compare-3.1")] #[test_case(PythonVersion::PY37, & [3, 1], true, true; "compare-3.1")]
#[test_case(PythonVersion::Py37, & [3, 5], true, true; "compare-3.5")] #[test_case(PythonVersion::PY37, & [3, 5], true, true; "compare-3.5")]
#[test_case(PythonVersion::Py37, & [3, 7], true, false; "compare-3.7")] #[test_case(PythonVersion::PY37, & [3, 7], true, false; "compare-3.7")]
#[test_case(PythonVersion::Py37, & [3, 7], false, true; "compare-3.7-not-equal")] #[test_case(PythonVersion::PY37, & [3, 7], false, true; "compare-3.7-not-equal")]
#[test_case(PythonVersion::Py37, & [3, 8], false, false; "compare-3.8")] #[test_case(PythonVersion::PY37, & [3, 8], false, false; "compare-3.8")]
#[test_case(PythonVersion::Py310, & [3, 9], true, true; "compare-3.9")] #[test_case(PythonVersion::PY310, & [3, 9], true, true; "compare-3.9")]
#[test_case(PythonVersion::Py310, & [3, 11], true, false; "compare-3.11")] #[test_case(PythonVersion::PY310, & [3, 11], true, false; "compare-3.11")]
fn test_compare_version( fn test_compare_version(
version: PythonVersion, version: PythonVersion,
target_versions: &[u8], target_versions: &[u8],

View file

@ -6,7 +6,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::fix::edits::{remove_argument, Parentheses}; use crate::fix::edits::{remove_argument, Parentheses};
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
use super::{ use super::{
check_type_vars, find_generic, in_nested_context, DisplayTypeVars, TypeVarReferenceVisitor, check_type_vars, find_generic, in_nested_context, DisplayTypeVars, TypeVarReferenceVisitor,
@ -106,7 +106,7 @@ impl Violation for NonPEP695GenericClass {
/// UP046 /// UP046
pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassDef) { pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassDef) {
// PEP-695 syntax is only available on Python 3.12+ // PEP-695 syntax is only available on Python 3.12+
if checker.settings.target_version < PythonVersion::Py312 { if checker.settings.target_version < PythonVersion::PY312 {
return; return;
} }

View file

@ -5,7 +5,7 @@ use ruff_python_ast::StmtFunctionDef;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
use super::{check_type_vars, in_nested_context, DisplayTypeVars, TypeVarReferenceVisitor}; use super::{check_type_vars, in_nested_context, DisplayTypeVars, TypeVarReferenceVisitor};
@ -98,7 +98,7 @@ impl Violation for NonPEP695GenericFunction {
/// UP047 /// UP047
pub(crate) fn non_pep695_generic_function(checker: &Checker, function_def: &StmtFunctionDef) { pub(crate) fn non_pep695_generic_function(checker: &Checker, function_def: &StmtFunctionDef) {
// PEP-695 syntax is only available on Python 3.12+ // PEP-695 syntax is only available on Python 3.12+
if checker.settings.target_version < PythonVersion::Py312 { if checker.settings.target_version < PythonVersion::PY312 {
return; return;
} }

View file

@ -9,7 +9,7 @@ use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssi
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
use super::{ use super::{
expr_name_to_type_var, DisplayTypeVars, TypeParamKind, TypeVar, TypeVarReferenceVisitor, expr_name_to_type_var, DisplayTypeVars, TypeParamKind, TypeVar, TypeVarReferenceVisitor,
@ -111,7 +111,7 @@ impl Violation for NonPEP695TypeAlias {
/// UP040 /// UP040
pub(crate) fn non_pep695_type_alias_type(checker: &Checker, stmt: &StmtAssign) { pub(crate) fn non_pep695_type_alias_type(checker: &Checker, stmt: &StmtAssign) {
if checker.settings.target_version < PythonVersion::Py312 { if checker.settings.target_version < PythonVersion::PY312 {
return; return;
} }
@ -182,7 +182,7 @@ pub(crate) fn non_pep695_type_alias_type(checker: &Checker, stmt: &StmtAssign) {
/// UP040 /// UP040
pub(crate) fn non_pep695_type_alias(checker: &Checker, stmt: &StmtAnnAssign) { pub(crate) fn non_pep695_type_alias(checker: &Checker, stmt: &StmtAnnAssign) {
if checker.settings.target_version < PythonVersion::Py312 { if checker.settings.target_version < PythonVersion::PY312 {
return; return;
} }

View file

@ -8,7 +8,7 @@ use ruff_python_ast::name::{Name, UnqualifiedName};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for uses of exceptions that alias `TimeoutError`. /// Checks for uses of exceptions that alias `TimeoutError`.
@ -62,7 +62,7 @@ fn is_alias(expr: &Expr, semantic: &SemanticModel, target_version: PythonVersion
semantic semantic
.resolve_qualified_name(expr) .resolve_qualified_name(expr)
.is_some_and(|qualified_name| { .is_some_and(|qualified_name| {
if target_version >= PythonVersion::Py311 { if target_version >= PythonVersion::PY311 {
matches!( matches!(
qualified_name.segments(), qualified_name.segments(),
["socket", "timeout"] | ["asyncio", "TimeoutError"] ["socket", "timeout"] | ["asyncio", "TimeoutError"]
@ -73,7 +73,7 @@ fn is_alias(expr: &Expr, semantic: &SemanticModel, target_version: PythonVersion
// fix in Python <3.10. We add an assert to make this assumption // fix in Python <3.10. We add an assert to make this assumption
// explicit. // explicit.
assert!( assert!(
target_version >= PythonVersion::Py310, target_version >= PythonVersion::PY310,
"lint should only be used for Python 3.10+", "lint should only be used for Python 3.10+",
); );
matches!(qualified_name.segments(), ["socket", "timeout"]) matches!(qualified_name.segments(), ["socket", "timeout"])

View file

@ -8,7 +8,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::importer::ImportRequest; use crate::importer::ImportRequest;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for the use of generics that can be replaced with standard library /// Checks for the use of generics that can be replaced with standard library
@ -98,7 +98,7 @@ pub(crate) fn use_pep585_annotation(checker: &Checker, expr: &Expr, replacement:
checker.semantic(), checker.semantic(),
)?; )?;
let binding_edit = Edit::range_replacement(binding, expr.range()); let binding_edit = Edit::range_replacement(binding, expr.range());
let applicability = if checker.settings.target_version >= PythonVersion::Py310 { let applicability = if checker.settings.target_version >= PythonVersion::PY310 {
Applicability::Safe Applicability::Safe
} else { } else {
Applicability::Unsafe Applicability::Unsafe
@ -122,7 +122,7 @@ pub(crate) fn use_pep585_annotation(checker: &Checker, expr: &Expr, replacement:
Ok(Fix::applicable_edits( Ok(Fix::applicable_edits(
import_edit, import_edit,
[reference_edit], [reference_edit],
if checker.settings.target_version >= PythonVersion::Py310 { if checker.settings.target_version >= PythonVersion::PY310 {
Applicability::Safe Applicability::Safe
} else { } else {
Applicability::Unsafe Applicability::Unsafe

View file

@ -3,6 +3,7 @@ use ruff_diagnostics::{
}; };
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::helpers::{pep_604_optional, pep_604_union}; use ruff_python_ast::helpers::{pep_604_optional, pep_604_union};
use ruff_python_ast::PythonVersion;
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::analyze::typing::Pep604Operator; use ruff_python_semantic::analyze::typing::Pep604Operator;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
@ -10,7 +11,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::codes::Rule; use crate::codes::Rule;
use crate::fix::edits::pad; use crate::fix::edits::pad;
use crate::settings::types::{PreviewMode, PythonVersion}; use crate::settings::types::PreviewMode;
/// ## What it does /// ## What it does
/// Check for type annotations that can be rewritten based on [PEP 604] syntax. /// Check for type annotations that can be rewritten based on [PEP 604] syntax.
@ -141,7 +142,7 @@ pub(crate) fn non_pep604_annotation(
&& !checker.semantic().in_complex_string_type_definition() && !checker.semantic().in_complex_string_type_definition()
&& is_allowed_value(slice); && is_allowed_value(slice);
let applicability = if checker.settings.target_version >= PythonVersion::Py310 { let applicability = if checker.settings.target_version >= PythonVersion::PY310 {
Applicability::Safe Applicability::Safe
} else { } else {
Applicability::Unsafe Applicability::Unsafe

View file

@ -1,9 +1,9 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{Expr, ExprSubscript}; use ruff_python_ast::{Expr, ExprSubscript, PythonVersion};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use crate::{checkers::ast::Checker, settings::types::PythonVersion}; use crate::checkers::ast::Checker;
/// ## What it does /// ## What it does
/// Checks for uses of `Unpack[]` on Python 3.11 and above, and suggests /// Checks for uses of `Unpack[]` on Python 3.11 and above, and suggests
@ -53,7 +53,7 @@ impl Violation for NonPEP646Unpack {
/// UP044 /// UP044
pub(crate) fn use_pep646_unpack(checker: &Checker, expr: &ExprSubscript) { pub(crate) fn use_pep646_unpack(checker: &Checker, expr: &ExprSubscript) {
if checker.settings.target_version < PythonVersion::Py311 { if checker.settings.target_version < PythonVersion::PY311 {
return; return;
} }

View file

@ -6,7 +6,7 @@ use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// Format a code snippet to call `name.method()`. /// Format a code snippet to call `name.method()`.
pub(super) fn generate_method_call(name: Name, method: &str, generator: Generator) -> String { pub(super) fn generate_method_call(name: Name, method: &str, generator: Generator) -> String {
@ -260,7 +260,7 @@ fn match_open_keywords(
if read_mode { if read_mode {
// newline is only valid for write_text // newline is only valid for write_text
return None; return None;
} else if target_version < PythonVersion::Py310 { } else if target_version < PythonVersion::PY310 {
// `pathlib` doesn't support `newline` until Python 3.10. // `pathlib` doesn't support `newline` until Python 3.10.
return None; return None;
} }

View file

@ -8,10 +8,11 @@ mod tests {
use std::path::Path; use std::path::Path;
use anyhow::Result; use anyhow::Result;
use ruff_python_ast::PythonVersion;
use test_case::test_case; use test_case::test_case;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::{PreviewMode, PythonVersion}; use crate::settings::types::PreviewMode;
use crate::test::test_path; use crate::test::test_path;
use crate::{assert_messages, settings}; use crate::{assert_messages, settings};
@ -84,7 +85,7 @@ mod tests {
let diagnostics = test_path( let diagnostics = test_path(
Path::new("refurb/FURB103.py"), Path::new("refurb/FURB103.py"),
&settings::LinterSettings::for_rule(Rule::WriteWholeFile) &settings::LinterSettings::for_rule(Rule::WriteWholeFile)
.with_target_version(PythonVersion::Py39), .with_target_version(PythonVersion::PY39),
)?; )?;
assert_messages!(diagnostics); assert_messages!(diagnostics);
Ok(()) Ok(())

View file

@ -1,10 +1,11 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::PythonVersion;
use ruff_python_ast::{self as ast, Expr, ExprAttribute, ExprCall}; use ruff_python_ast::{self as ast, Expr, ExprAttribute, ExprCall};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::snippet::SourceCodeSnippet; use crate::fix::snippet::SourceCodeSnippet;
use crate::{checkers::ast::Checker, settings::types::PythonVersion};
/// ## What it does /// ## What it does
/// Checks for uses of `bin(...).count("1")` to perform a population count. /// Checks for uses of `bin(...).count("1")` to perform a population count.
@ -58,7 +59,7 @@ impl AlwaysFixableViolation for BitCount {
/// FURB161 /// FURB161
pub(crate) fn bit_count(checker: &Checker, call: &ExprCall) { pub(crate) fn bit_count(checker: &Checker, call: &ExprCall) {
// `int.bit_count()` was added in Python 3.10 // `int.bit_count()` was added in Python 3.10
if checker.settings.target_version < PythonVersion::Py310 { if checker.settings.target_version < PythonVersion::PY310 {
return; return;
} }

View file

@ -3,13 +3,12 @@ use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_ast::{ use ruff_python_ast::{
Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp, Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp,
Number, Operator, UnaryOp, Number, Operator, PythonVersion, UnaryOp,
}; };
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::settings::types::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for `datetime.fromisoformat()` calls /// Checks for `datetime.fromisoformat()` calls
@ -83,7 +82,7 @@ impl AlwaysFixableViolation for FromisoformatReplaceZ {
/// FURB162 /// FURB162
pub(crate) fn fromisoformat_replace_z(checker: &Checker, call: &ExprCall) { pub(crate) fn fromisoformat_replace_z(checker: &Checker, call: &ExprCall) {
if checker.settings.target_version < PythonVersion::Py311 { if checker.settings.target_version < PythonVersion::PY311 {
return; return;
} }

View file

@ -1,11 +1,11 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast as ast; use ruff_python_ast::{self as ast, PythonVersion};
use ruff_python_semantic::SemanticModel; use ruff_python_semantic::SemanticModel;
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::Locator; use crate::Locator;
use crate::{checkers::ast::Checker, settings::types::PythonVersion};
/// ## What it does /// ## What it does
/// Checks for code that could be written more idiomatically using /// Checks for code that could be written more idiomatically using
@ -69,7 +69,7 @@ impl AlwaysFixableViolation for SliceToRemovePrefixOrSuffix {
/// FURB188 /// FURB188
pub(crate) fn slice_to_remove_affix_expr(checker: &Checker, if_expr: &ast::ExprIf) { pub(crate) fn slice_to_remove_affix_expr(checker: &Checker, if_expr: &ast::ExprIf) {
if checker.settings.target_version < PythonVersion::Py39 { if checker.settings.target_version < PythonVersion::PY39 {
return; return;
} }
@ -100,7 +100,7 @@ pub(crate) fn slice_to_remove_affix_expr(checker: &Checker, if_expr: &ast::ExprI
/// FURB188 /// FURB188
pub(crate) fn slice_to_remove_affix_stmt(checker: &Checker, if_stmt: &ast::StmtIf) { pub(crate) fn slice_to_remove_affix_stmt(checker: &Checker, if_stmt: &ast::StmtIf) {
if checker.settings.target_version < PythonVersion::Py39 { if checker.settings.target_version < PythonVersion::PY39 {
return; return;
} }
if let Some(removal_data) = affix_removal_data_stmt(if_stmt) { if let Some(removal_data) = affix_removal_data_stmt(if_stmt) {

View file

@ -11,15 +11,14 @@ mod tests {
use anyhow::Result; use anyhow::Result;
use regex::Regex; use regex::Regex;
use ruff_python_ast::PythonVersion;
use ruff_source_file::SourceFileBuilder; use ruff_source_file::SourceFileBuilder;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use test_case::test_case; use test_case::test_case;
use crate::pyproject_toml::lint_pyproject_toml; use crate::pyproject_toml::lint_pyproject_toml;
use crate::registry::Rule; use crate::registry::Rule;
use crate::settings::types::{ use crate::settings::types::{CompiledPerFileIgnoreList, PerFileIgnore, PreviewMode};
CompiledPerFileIgnoreList, PerFileIgnore, PreviewMode, PythonVersion,
};
use crate::settings::LinterSettings; use crate::settings::LinterSettings;
use crate::test::{test_path, test_resource_path}; use crate::test::{test_path, test_resource_path};
use crate::{assert_messages, settings}; use crate::{assert_messages, settings};
@ -130,7 +129,7 @@ mod tests {
extend_markup_names: vec![], extend_markup_names: vec![],
allowed_markup_calls: vec![], allowed_markup_calls: vec![],
}, },
target_version: PythonVersion::Py310, target_version: PythonVersion::PY310,
..LinterSettings::for_rule(Rule::IncorrectlyParenthesizedTupleInSubscript) ..LinterSettings::for_rule(Rule::IncorrectlyParenthesizedTupleInSubscript)
}, },
)?; )?;
@ -149,7 +148,7 @@ 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::for_rule(Rule::ImplicitOptional) &settings::LinterSettings::for_rule(Rule::ImplicitOptional)
.with_target_version(PythonVersion::Py39), .with_target_version(PythonVersion::PY39),
)?; )?;
assert_messages!(snapshot, diagnostics); assert_messages!(snapshot, diagnostics);
Ok(()) Ok(())

View file

@ -13,7 +13,7 @@ use crate::fix::edits::{remove_argument, Parentheses};
use crate::rules::pyupgrade::rules::pep695::{ use crate::rules::pyupgrade::rules::pep695::{
expr_name_to_type_var, find_generic, DisplayTypeVars, TypeParamKind, TypeVar, expr_name_to_type_var, find_generic, DisplayTypeVars, TypeParamKind, TypeVar,
}; };
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
/// ## What it does /// ## What it does
/// Checks for classes that have [PEP 695] [type parameter lists] /// Checks for classes that have [PEP 695] [type parameter lists]
@ -79,7 +79,7 @@ impl Violation for ClassWithMixedTypeVars {
/// RUF053 /// RUF053
pub(crate) fn class_with_mixed_type_vars(checker: &Checker, class_def: &StmtClassDef) { pub(crate) fn class_with_mixed_type_vars(checker: &Checker, class_def: &StmtClassDef) {
if checker.settings.target_version < PythonVersion::Py312 { if checker.settings.target_version < PythonVersion::PY312 {
return; return;
} }

View file

@ -12,7 +12,7 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::importer::ImportRequest; use crate::importer::ImportRequest;
use crate::settings::types::PythonVersion; use ruff_python_ast::PythonVersion;
use super::super::typing::type_hint_explicitly_allows_none; use super::super::typing::type_hint_explicitly_allows_none;
@ -112,7 +112,7 @@ impl fmt::Display for ConversionType {
impl From<PythonVersion> for ConversionType { impl From<PythonVersion> for ConversionType {
fn from(target_version: PythonVersion) -> Self { fn from(target_version: PythonVersion) -> Self {
if target_version >= PythonVersion::Py310 { if target_version >= PythonVersion::PY310 {
Self::BinOpOr Self::BinOpOr
} else { } else {
Self::Optional Self::Optional
@ -177,7 +177,7 @@ pub(crate) fn implicit_optional(checker: &Checker, parameters: &Parameters) {
let Some(expr) = type_hint_explicitly_allows_none( let Some(expr) = type_hint_explicitly_allows_none(
parsed_annotation.expression(), parsed_annotation.expression(),
checker, checker,
checker.settings.target_version.minor(), checker.settings.target_version.minor,
) else { ) else {
continue; continue;
}; };
@ -195,7 +195,7 @@ pub(crate) fn implicit_optional(checker: &Checker, parameters: &Parameters) {
let Some(expr) = type_hint_explicitly_allows_none( let Some(expr) = type_hint_explicitly_allows_none(
annotation, annotation,
checker, checker,
checker.settings.target_version.minor(), checker.settings.target_version.minor,
) else { ) else {
continue; continue;
}; };

View file

@ -1,9 +1,9 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{Expr, ExprSubscript}; use ruff_python_ast::{Expr, ExprSubscript, PythonVersion};
use ruff_text_size::Ranged; use ruff_text_size::Ranged;
use crate::{checkers::ast::Checker, settings::types::PythonVersion}; use crate::checkers::ast::Checker;
/// ## What it does /// ## What it does
/// Checks for consistent style regarding whether nonempty tuples in subscripts /// Checks for consistent style regarding whether nonempty tuples in subscripts
@ -88,7 +88,7 @@ pub(crate) fn subscript_with_parenthesized_tuple(checker: &Checker, subscript: &
// to a syntax error in Python 3.10. // to a syntax error in Python 3.10.
// This is no longer a syntax error starting in Python 3.11 // This is no longer a syntax error starting in Python 3.11
// see https://peps.python.org/pep-0646/#change-1-star-expressions-in-indexes // see https://peps.python.org/pep-0646/#change-1-star-expressions-in-indexes
if checker.settings.target_version <= PythonVersion::Py310 if checker.settings.target_version <= PythonVersion::PY310
&& !prefer_parentheses && !prefer_parentheses
&& tuple_subscript.iter().any(Expr::is_starred_expr) && tuple_subscript.iter().any(Expr::is_starred_expr)
{ {

View file

@ -11,6 +11,7 @@ use std::sync::LazyLock;
use crate::codes::RuleCodePrefix; use crate::codes::RuleCodePrefix;
use ruff_macros::CacheKey; use ruff_macros::CacheKey;
use ruff_python_ast::PythonVersion;
use crate::line_width::LineLength; use crate::line_width::LineLength;
use crate::registry::{Linter, Rule}; use crate::registry::{Linter, Rule};
@ -21,9 +22,7 @@ use crate::rules::{
flake8_self, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe, flake8_self, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, isort, mccabe,
pep8_naming, pycodestyle, pydoclint, pydocstyle, pyflakes, pylint, pyupgrade, ruff, pep8_naming, pycodestyle, pydoclint, pydocstyle, pyflakes, pylint, pyupgrade, ruff,
}; };
use crate::settings::types::{ use crate::settings::types::{CompiledPerFileIgnoreList, ExtensionMapping, FilePatternSet};
CompiledPerFileIgnoreList, ExtensionMapping, FilePatternSet, PythonVersion,
};
use crate::{codes, RuleSelector}; use crate::{codes, RuleSelector};
use super::line_width::IndentWidth; use super::line_width::IndentWidth;
@ -282,7 +281,7 @@ impl Display for LinterSettings {
self.per_file_ignores, self.per_file_ignores,
self.fix_safety | nested, self.fix_safety | nested,
self.target_version | debug, self.target_version,
self.preview, self.preview,
self.explicit_preview_rules, self.explicit_preview_rules,
self.extension | debug, self.extension | debug,

View file

@ -7,55 +7,66 @@ use std::string::ToString;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder}; use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
use log::debug; use pep440_rs::{VersionSpecifier, VersionSpecifiers};
use pep440_rs::{Operator, Version as Pep440Version, Version, VersionSpecifier, VersionSpecifiers};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::{de, Deserialize, Deserializer, Serialize}; use serde::{de, Deserialize, Deserializer, Serialize};
use strum::IntoEnumIterator;
use strum_macros::EnumIter; use strum_macros::EnumIter;
use ruff_cache::{CacheKey, CacheKeyHasher}; use ruff_cache::{CacheKey, CacheKeyHasher};
use ruff_diagnostics::Applicability; use ruff_diagnostics::Applicability;
use ruff_macros::CacheKey; use ruff_macros::CacheKey;
use ruff_python_ast::PySourceType; use ruff_python_ast::{self as ast, PySourceType};
use crate::registry::RuleSet; use crate::registry::RuleSet;
use crate::rule_selector::RuleSelector; use crate::rule_selector::RuleSelector;
use crate::{display_settings, fs}; use crate::{display_settings, fs};
#[derive( #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
Clone,
Copy,
Debug,
PartialOrd,
Ord,
PartialEq,
Eq,
Default,
Serialize,
Deserialize,
CacheKey,
EnumIter,
)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum PythonVersion { pub enum PythonVersion {
Py37, Py37,
Py38, Py38,
// Make sure to also change the default for `ruff_python_formatter::PythonVersion`
// when changing the default here.
#[default]
Py39, Py39,
Py310, Py310,
Py311, Py311,
Py312, Py312,
Py313, Py313,
// Remember to update the `latest()` function
// when adding new versions here!
} }
impl From<PythonVersion> for Pep440Version { impl Default for PythonVersion {
fn default() -> Self {
// SAFETY: the unit test `default_python_version_works()` checks that this doesn't panic
Self::try_from(ast::PythonVersion::default()).unwrap()
}
}
impl TryFrom<ast::PythonVersion> for PythonVersion {
type Error = String;
fn try_from(value: ast::PythonVersion) -> Result<Self, Self::Error> {
match value {
ast::PythonVersion::PY37 => Ok(Self::Py37),
ast::PythonVersion::PY38 => Ok(Self::Py38),
ast::PythonVersion::PY39 => Ok(Self::Py39),
ast::PythonVersion::PY310 => Ok(Self::Py310),
ast::PythonVersion::PY311 => Ok(Self::Py311),
ast::PythonVersion::PY312 => Ok(Self::Py312),
ast::PythonVersion::PY313 => Ok(Self::Py313),
_ => Err(format!("unrecognized python version {value}")),
}
}
}
impl From<PythonVersion> for ast::PythonVersion {
fn from(value: PythonVersion) -> Self {
let (major, minor) = value.as_tuple();
Self { major, minor }
}
}
impl From<PythonVersion> for pep440_rs::Version {
fn from(version: PythonVersion) -> Self { fn from(version: PythonVersion) -> Self {
let (major, minor) = version.as_tuple(); let (major, minor) = version.as_tuple();
Self::new([u64::from(major), u64::from(minor)]) Self::new([u64::from(major), u64::from(minor)])
@ -63,15 +74,6 @@ impl From<PythonVersion> for Pep440Version {
} }
impl PythonVersion { impl PythonVersion {
/// Return the latest supported Python version.
pub const fn latest() -> Self {
Self::Py313
}
pub const fn minimal_supported() -> Self {
Self::Py37
}
pub const fn as_tuple(&self) -> (u8, u8) { pub const fn as_tuple(&self) -> (u8, u8) {
match self { match self {
Self::Py37 => (3, 7), Self::Py37 => (3, 7),
@ -83,53 +85,6 @@ impl PythonVersion {
Self::Py313 => (3, 13), Self::Py313 => (3, 13),
} }
} }
pub const fn major(&self) -> u8 {
self.as_tuple().0
}
pub const fn minor(&self) -> u8 {
self.as_tuple().1
}
/// Infer the minimum supported [`PythonVersion`] from a `requires-python` specifier.
pub fn get_minimum_supported_version(requires_version: &VersionSpecifiers) -> Option<Self> {
/// Truncate a version to its major and minor components.
fn major_minor(version: &Version) -> Option<Version> {
let major = version.release().first()?;
let minor = version.release().get(1)?;
Some(Version::new([major, minor]))
}
// Extract the minimum supported version from the specifiers.
let minimum_version = requires_version
.iter()
.filter(|specifier| {
matches!(
specifier.operator(),
Operator::Equal
| Operator::EqualStar
| Operator::ExactEqual
| Operator::TildeEqual
| Operator::GreaterThan
| Operator::GreaterThanEqual
)
})
.filter_map(|specifier| major_minor(specifier.version()))
.min()?;
debug!("Detected minimum supported `requires-python` version: {minimum_version}");
// Find the Python version that matches the minimum supported version.
PythonVersion::iter().find(|version| Version::from(*version) == minimum_version)
}
/// Return `true` if the current version supports [PEP 701].
///
/// [PEP 701]: https://peps.python.org/pep-0701/
pub fn supports_pep701(self) -> bool {
self >= Self::Py312
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, CacheKey, is_macro::Is)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, CacheKey, is_macro::Is)]
@ -679,3 +634,11 @@ impl Deref for CompiledPerFileIgnoreList {
&self.ignores &self.ignores
} }
} }
#[cfg(test)]
mod tests {
#[test]
fn default_python_version_works() {
super::PythonVersion::default();
}
}

View file

@ -6,6 +6,7 @@ pub use generated::*;
pub use int::*; pub use int::*;
pub use nodes::*; pub use nodes::*;
pub use operator_precedence::*; pub use operator_precedence::*;
pub use python_version::*;
pub mod comparable; pub mod comparable;
pub mod docstrings; pub mod docstrings;
@ -19,7 +20,7 @@ mod node;
mod nodes; mod nodes;
pub mod operator_precedence; pub mod operator_precedence;
pub mod parenthesize; pub mod parenthesize;
pub mod python_version; mod python_version;
pub mod relocate; pub mod relocate;
pub mod script; pub mod script;
pub mod statement_visitor; pub mod statement_visitor;

View file

@ -4,6 +4,7 @@ use std::fmt;
/// ///
/// N.B. This does not necessarily represent a Python version that we actually support. /// N.B. This does not necessarily represent a Python version that we actually support.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "cache", derive(ruff_macros::CacheKey))]
pub struct PythonVersion { pub struct PythonVersion {
pub major: u8, pub major: u8,
pub minor: u8, pub minor: u8,
@ -43,9 +44,24 @@ impl PythonVersion {
.into_iter() .into_iter()
} }
pub const fn latest() -> Self {
Self::PY313
}
pub const fn as_tuple(self) -> (u8, u8) {
(self.major, self.minor)
}
pub fn free_threaded_build_available(self) -> bool { pub fn free_threaded_build_available(self) -> bool {
self >= PythonVersion::PY313 self >= PythonVersion::PY313
} }
/// Return `true` if the current version supports [PEP 701].
///
/// [PEP 701]: https://peps.python.org/pep-0701/
pub fn supports_pep_701(self) -> bool {
self >= Self::PY312
}
} }
impl Default for PythonVersion { impl Default for PythonVersion {

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py38"} {"target_version": "3.8"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py311"} {"target_version": "3.11"}

View file

@ -1 +1 @@
{"target_version": "py38"} {"target_version": "3.8"}

View file

@ -1 +1 @@
{"preview": "enabled", "target_version": "py310"} {"preview": "enabled", "target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"target_version": "py310"} {"target_version": "3.10"}

View file

@ -1 +1 @@
{"preview": "enabled", "target_version": "py310"} {"preview": "enabled", "target_version": "3.10"}

Some files were not shown because too many files have changed in this diff Show more