mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 06:41:23 +00:00
Add Checker::import_from_typing
(#17340)
Summary -- This PR replaces uses of version-dependent imports from `typing` or `typing_extensions` with a centralized `Checker::import_from_typing` method. The idea here is to make the fix for #9761 (whatever it ends up being) applicable to all of the rules performing similar checks. Test Plan -- Existing tests for the affected rules.
This commit is contained in:
parent
1aad180aae
commit
8e11c53310
13 changed files with 87 additions and 140 deletions
|
@ -31,7 +31,7 @@ use ruff_python_parser::semantic_errors::{
|
||||||
};
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, IsolationLevel};
|
use ruff_diagnostics::{Diagnostic, Edit, IsolationLevel};
|
||||||
use ruff_notebook::{CellOffsets, NotebookIndex};
|
use ruff_notebook::{CellOffsets, NotebookIndex};
|
||||||
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
|
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
|
||||||
use ruff_python_ast::identifier::Identifier;
|
use ruff_python_ast::identifier::Identifier;
|
||||||
|
@ -62,7 +62,7 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::annotation::AnnotationContext;
|
use crate::checkers::ast::annotation::AnnotationContext;
|
||||||
use crate::docstrings::extraction::ExtractionTarget;
|
use crate::docstrings::extraction::ExtractionTarget;
|
||||||
use crate::importer::Importer;
|
use crate::importer::{ImportRequest, Importer, ResolutionError};
|
||||||
use crate::noqa::NoqaMapping;
|
use crate::noqa::NoqaMapping;
|
||||||
use crate::package::PackageRoot;
|
use crate::package::PackageRoot;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
@ -530,6 +530,28 @@ impl<'a> Checker<'a> {
|
||||||
f(&mut checker, self);
|
f(&mut checker, self);
|
||||||
self.semantic_checker = checker;
|
self.semantic_checker = checker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to create an [`Edit`] that imports `member`.
|
||||||
|
///
|
||||||
|
/// On Python <`version_added_to_typing`, `member` is imported from `typing_extensions`, while
|
||||||
|
/// on Python >=`version_added_to_typing`, it is imported from `typing`.
|
||||||
|
///
|
||||||
|
/// See [`Importer::get_or_import_symbol`] for more details on the returned values.
|
||||||
|
pub(crate) fn import_from_typing(
|
||||||
|
&self,
|
||||||
|
member: &str,
|
||||||
|
position: TextSize,
|
||||||
|
version_added_to_typing: PythonVersion,
|
||||||
|
) -> Result<(Edit, String), ResolutionError> {
|
||||||
|
let source_module = if self.target_version() >= version_added_to_typing {
|
||||||
|
"typing"
|
||||||
|
} else {
|
||||||
|
"typing_extensions"
|
||||||
|
};
|
||||||
|
let request = ImportRequest::import_from(source_module, member);
|
||||||
|
self.importer()
|
||||||
|
.get_or_import_symbol(&request, position, self.semantic())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SemanticSyntaxContext for Checker<'_> {
|
impl SemanticSyntaxContext for Checker<'_> {
|
||||||
|
|
|
@ -6,7 +6,6 @@ use ruff_python_semantic::Modules;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::importer::ImportRequest;
|
|
||||||
use crate::rules::fastapi::rules::is_fastapi_route;
|
use crate::rules::fastapi::rules::is_fastapi_route;
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
|
||||||
|
@ -232,15 +231,10 @@ fn create_diagnostic(
|
||||||
);
|
);
|
||||||
|
|
||||||
let try_generate_fix = || {
|
let try_generate_fix = || {
|
||||||
let module = if checker.target_version() >= PythonVersion::PY39 {
|
let (import_edit, binding) = checker.import_from_typing(
|
||||||
"typing"
|
"Annotated",
|
||||||
} else {
|
|
||||||
"typing_extensions"
|
|
||||||
};
|
|
||||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
|
||||||
&ImportRequest::import_from(module, "Annotated"),
|
|
||||||
parameter.range.start(),
|
parameter.range.start(),
|
||||||
checker.semantic(),
|
PythonVersion::PY39,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Each of these classes takes a single, optional default
|
// Each of these classes takes a single, optional default
|
||||||
|
|
|
@ -14,7 +14,7 @@ use ruff_python_semantic::analyze::visibility;
|
||||||
use ruff_python_semantic::{Definition, SemanticModel};
|
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::checkers::ast::Checker;
|
||||||
use ruff_python_ast::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.
|
||||||
|
@ -119,26 +119,19 @@ impl AutoPythonType {
|
||||||
/// additional edits.
|
/// additional edits.
|
||||||
pub(crate) fn into_expression(
|
pub(crate) fn into_expression(
|
||||||
self,
|
self,
|
||||||
importer: &Importer,
|
checker: &Checker,
|
||||||
at: TextSize,
|
at: TextSize,
|
||||||
semantic: &SemanticModel,
|
|
||||||
target_version: PythonVersion,
|
|
||||||
) -> Option<(Expr, Vec<Edit>)> {
|
) -> Option<(Expr, Vec<Edit>)> {
|
||||||
|
let target_version = checker.target_version();
|
||||||
match self {
|
match self {
|
||||||
AutoPythonType::Never => {
|
AutoPythonType::Never => {
|
||||||
let (no_return_edit, binding) = importer
|
let member = if target_version >= PythonVersion::PY311 {
|
||||||
.get_or_import_symbol(
|
|
||||||
&ImportRequest::import_from(
|
|
||||||
"typing",
|
|
||||||
if target_version >= PythonVersion::PY311 {
|
|
||||||
"Never"
|
"Never"
|
||||||
} else {
|
} else {
|
||||||
"NoReturn"
|
"NoReturn"
|
||||||
},
|
};
|
||||||
),
|
let (no_return_edit, binding) = checker
|
||||||
at,
|
.import_from_typing(member, at, PythonVersion::lowest())
|
||||||
semantic,
|
|
||||||
)
|
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let expr = Expr::Name(ast::ExprName {
|
let expr = Expr::Name(ast::ExprName {
|
||||||
id: Name::from(binding),
|
id: Name::from(binding),
|
||||||
|
@ -175,12 +168,8 @@ impl AutoPythonType {
|
||||||
let element = type_expr(*python_type)?;
|
let element = type_expr(*python_type)?;
|
||||||
|
|
||||||
// Ex) `Optional[int]`
|
// Ex) `Optional[int]`
|
||||||
let (optional_edit, binding) = importer
|
let (optional_edit, binding) = checker
|
||||||
.get_or_import_symbol(
|
.import_from_typing("Optional", at, PythonVersion::lowest())
|
||||||
&ImportRequest::import_from("typing", "Optional"),
|
|
||||||
at,
|
|
||||||
semantic,
|
|
||||||
)
|
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let expr = typing_optional(element, Name::from(binding));
|
let expr = typing_optional(element, Name::from(binding));
|
||||||
Some((expr, vec![optional_edit]))
|
Some((expr, vec![optional_edit]))
|
||||||
|
@ -192,12 +181,8 @@ impl AutoPythonType {
|
||||||
.collect::<Option<Vec<_>>>()?;
|
.collect::<Option<Vec<_>>>()?;
|
||||||
|
|
||||||
// Ex) `Union[int, str]`
|
// Ex) `Union[int, str]`
|
||||||
let (union_edit, binding) = importer
|
let (union_edit, binding) = checker
|
||||||
.get_or_import_symbol(
|
.import_from_typing("Union", at, PythonVersion::lowest())
|
||||||
&ImportRequest::import_from("typing", "Union"),
|
|
||||||
at,
|
|
||||||
semantic,
|
|
||||||
)
|
|
||||||
.ok()?;
|
.ok()?;
|
||||||
let expr = typing_union(&elements, Name::from(binding));
|
let expr = typing_union(&elements, Name::from(binding));
|
||||||
Some((expr, vec![union_edit]))
|
Some((expr, vec![union_edit]))
|
||||||
|
|
|
@ -721,12 +721,7 @@ pub(crate) fn definition(
|
||||||
} else {
|
} else {
|
||||||
auto_return_type(function)
|
auto_return_type(function)
|
||||||
.and_then(|return_type| {
|
.and_then(|return_type| {
|
||||||
return_type.into_expression(
|
return_type.into_expression(checker, function.parameters.start())
|
||||||
checker.importer(),
|
|
||||||
function.parameters.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
checker.target_version(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
|
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
|
||||||
};
|
};
|
||||||
|
@ -752,12 +747,7 @@ pub(crate) fn definition(
|
||||||
} else {
|
} else {
|
||||||
auto_return_type(function)
|
auto_return_type(function)
|
||||||
.and_then(|return_type| {
|
.and_then(|return_type| {
|
||||||
return_type.into_expression(
|
return_type.into_expression(checker, function.parameters.start())
|
||||||
checker.importer(),
|
|
||||||
function.parameters.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
checker.target_version(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
|
.map(|(return_type, edits)| (checker.generator().expr(&return_type), edits))
|
||||||
};
|
};
|
||||||
|
@ -822,12 +812,8 @@ pub(crate) fn definition(
|
||||||
} else {
|
} else {
|
||||||
auto_return_type(function)
|
auto_return_type(function)
|
||||||
.and_then(|return_type| {
|
.and_then(|return_type| {
|
||||||
return_type.into_expression(
|
return_type
|
||||||
checker.importer(),
|
.into_expression(checker, function.parameters.start())
|
||||||
function.parameters.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
checker.target_version(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.map(|(return_type, edits)| {
|
.map(|(return_type, edits)| {
|
||||||
(checker.generator().expr(&return_type), edits)
|
(checker.generator().expr(&return_type), edits)
|
||||||
|
@ -861,12 +847,8 @@ pub(crate) fn definition(
|
||||||
} else {
|
} else {
|
||||||
auto_return_type(function)
|
auto_return_type(function)
|
||||||
.and_then(|return_type| {
|
.and_then(|return_type| {
|
||||||
return_type.into_expression(
|
return_type
|
||||||
checker.importer(),
|
.into_expression(checker, function.parameters.start())
|
||||||
function.parameters.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
checker.target_version(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.map(|(return_type, edits)| {
|
.map(|(return_type, edits)| {
|
||||||
(checker.generator().expr(&return_type), edits)
|
(checker.generator().expr(&return_type), edits)
|
||||||
|
|
|
@ -8,10 +8,9 @@ use ruff_python_semantic::analyze::class::is_metaclass;
|
||||||
use ruff_python_semantic::analyze::function_type::{self, FunctionType};
|
use ruff_python_semantic::analyze::function_type::{self, FunctionType};
|
||||||
use ruff_python_semantic::analyze::visibility::{is_abstract, is_overload};
|
use ruff_python_semantic::analyze::visibility::{is_abstract, is_overload};
|
||||||
use ruff_python_semantic::{Binding, ResolvedReference, ScopeId, SemanticModel};
|
use ruff_python_semantic::{Binding, ResolvedReference, ScopeId, SemanticModel};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::importer::{ImportRequest, ResolutionError};
|
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
|
@ -317,7 +316,8 @@ fn replace_custom_typevar_with_self(
|
||||||
self_or_cls_annotation: &ast::Expr,
|
self_or_cls_annotation: &ast::Expr,
|
||||||
) -> anyhow::Result<Fix> {
|
) -> anyhow::Result<Fix> {
|
||||||
// (1) Import `Self` (if necessary)
|
// (1) Import `Self` (if necessary)
|
||||||
let (import_edit, self_symbol_binding) = import_self(checker, function_def.start())?;
|
let (import_edit, self_symbol_binding) =
|
||||||
|
checker.import_from_typing("Self", function_def.start(), PythonVersion::PY311)?;
|
||||||
|
|
||||||
// (2) Remove the first parameter's annotation
|
// (2) Remove the first parameter's annotation
|
||||||
let mut other_edits = vec![Edit::deletion(
|
let mut other_edits = vec![Edit::deletion(
|
||||||
|
@ -367,24 +367,6 @@ fn replace_custom_typevar_with_self(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to create an [`Edit`] that imports `Self`.
|
|
||||||
///
|
|
||||||
/// On Python <3.11, `Self` is imported from `typing_extensions`;
|
|
||||||
/// on Python >=3.11, it is imported from `typing`.
|
|
||||||
/// 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.
|
|
||||||
fn import_self(checker: &Checker, position: TextSize) -> Result<(Edit, String), ResolutionError> {
|
|
||||||
let source_module = if checker.target_version() >= PythonVersion::PY311 {
|
|
||||||
"typing"
|
|
||||||
} else {
|
|
||||||
"typing_extensions"
|
|
||||||
};
|
|
||||||
let request = ImportRequest::import_from(source_module, "Self");
|
|
||||||
checker
|
|
||||||
.importer()
|
|
||||||
.get_or_import_symbol(&request, position, checker.semantic())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a series of [`Edit`]s that modify all references to the given `typevar`.
|
/// Returns a series of [`Edit`]s that modify all references to the given `typevar`.
|
||||||
///
|
///
|
||||||
/// Only references within `editable_range` will be modified.
|
/// Only references within `editable_range` will be modified.
|
||||||
|
|
|
@ -2,18 +2,19 @@ use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ruff_python_ast::name::Name;
|
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
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::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::{Expr, ExprBinOp, ExprContext, ExprName, ExprSubscript, ExprTuple, Operator};
|
use ruff_python_ast::name::Name;
|
||||||
|
use ruff_python_ast::{
|
||||||
|
Expr, ExprBinOp, ExprContext, ExprName, ExprSubscript, ExprTuple, Operator, PythonVersion,
|
||||||
|
};
|
||||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::importer::ImportRequest;
|
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for duplicate union members.
|
/// Checks for duplicate union members.
|
||||||
|
@ -181,11 +182,8 @@ fn generate_union_fix(
|
||||||
debug_assert!(nodes.len() >= 2, "At least two nodes required");
|
debug_assert!(nodes.len() >= 2, "At least two nodes required");
|
||||||
|
|
||||||
// Request `typing.Union`
|
// Request `typing.Union`
|
||||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
let (import_edit, binding) =
|
||||||
&ImportRequest::import_from("typing", "Union"),
|
checker.import_from_typing("Union", annotation.start(), PythonVersion::lowest())?;
|
||||||
annotation.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Construct the expression as `Subscript[typing.Union, Tuple[expr, [expr, ...]]]`
|
// Construct the expression as `Subscript[typing.Union, Tuple[expr, [expr, ...]]]`
|
||||||
let new_expr = Expr::Subscript(ExprSubscript {
|
let new_expr = Expr::Subscript(ExprSubscript {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::importer::ImportRequest;
|
|
||||||
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;
|
||||||
|
@ -214,17 +213,8 @@ fn replace_with_self_fix(
|
||||||
) -> anyhow::Result<Fix> {
|
) -> anyhow::Result<Fix> {
|
||||||
let semantic = checker.semantic();
|
let semantic = checker.semantic();
|
||||||
|
|
||||||
let (self_import, self_binding) = {
|
let (self_import, self_binding) =
|
||||||
let source_module = if checker.target_version() >= PythonVersion::PY311 {
|
checker.import_from_typing("Self", returns.start(), PythonVersion::PY311)?;
|
||||||
"typing"
|
|
||||||
} else {
|
|
||||||
"typing_extensions"
|
|
||||||
};
|
|
||||||
|
|
||||||
let (importer, semantic) = (checker.importer(), checker.semantic());
|
|
||||||
let request = ImportRequest::import_from(source_module, "Self");
|
|
||||||
importer.get_or_import_symbol(&request, returns.start(), semantic)?
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut others = Vec::with_capacity(2);
|
let mut others = Vec::with_capacity(2);
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::{checkers::ast::Checker, importer::ImportRequest};
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for redundant `Literal[None]` annotations.
|
/// Checks for redundant `Literal[None]` annotations.
|
||||||
|
@ -225,10 +225,10 @@ fn create_fix(
|
||||||
|
|
||||||
let fix = match union_kind {
|
let fix = match union_kind {
|
||||||
UnionKind::TypingOptional => {
|
UnionKind::TypingOptional => {
|
||||||
let (import_edit, bound_name) = checker.importer().get_or_import_symbol(
|
let (import_edit, bound_name) = checker.import_from_typing(
|
||||||
&ImportRequest::import_from("typing", "Optional"),
|
"Optional",
|
||||||
literal_expr.start(),
|
literal_expr.start(),
|
||||||
checker.semantic(),
|
PythonVersion::lowest(),
|
||||||
)?;
|
)?;
|
||||||
let optional_expr = typing_optional(new_literal_expr, Name::from(bound_name));
|
let optional_expr = typing_optional(new_literal_expr, Name::from(bound_name));
|
||||||
let content = checker.generator().expr(&optional_expr);
|
let content = checker.generator().expr(&optional_expr);
|
||||||
|
|
|
@ -6,12 +6,12 @@ use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Vi
|
||||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
name::Name, AnyParameterRef, Expr, ExprBinOp, ExprContext, ExprName, ExprSubscript, ExprTuple,
|
name::Name, AnyParameterRef, Expr, ExprBinOp, ExprContext, ExprName, ExprSubscript, ExprTuple,
|
||||||
Operator, Parameters,
|
Operator, Parameters, PythonVersion,
|
||||||
};
|
};
|
||||||
use ruff_python_semantic::analyze::typing::traverse_union;
|
use ruff_python_semantic::analyze::typing::traverse_union;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::{checkers::ast::Checker, importer::ImportRequest};
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for parameter annotations that contain redundant unions between
|
/// Checks for parameter annotations that contain redundant unions between
|
||||||
|
@ -268,11 +268,8 @@ fn generate_union_fix(
|
||||||
debug_assert!(nodes.len() >= 2, "At least two nodes required");
|
debug_assert!(nodes.len() >= 2, "At least two nodes required");
|
||||||
|
|
||||||
// Request `typing.Union`
|
// Request `typing.Union`
|
||||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
let (import_edit, binding) =
|
||||||
&ImportRequest::import_from("typing", "Union"),
|
checker.import_from_typing("Optional", annotation.start(), PythonVersion::lowest())?;
|
||||||
annotation.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Construct the expression as `Subscript[typing.Union, Tuple[expr, [expr, ...]]]`
|
// Construct the expression as `Subscript[typing.Union, Tuple[expr, [expr, ...]]]`
|
||||||
let new_expr = Expr::Subscript(ExprSubscript {
|
let new_expr = Expr::Subscript(ExprSubscript {
|
||||||
|
|
|
@ -6,7 +6,6 @@ use ruff_python_semantic::{analyze::class::is_enumeration, ScopeKind, SemanticMo
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::importer::ImportRequest;
|
|
||||||
use crate::rules::flake8_pyi::rules::TypingModule;
|
use crate::rules::flake8_pyi::rules::TypingModule;
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
@ -682,11 +681,8 @@ pub(crate) fn type_alias_without_annotation(checker: &Checker, value: &Expr, tar
|
||||||
target.range(),
|
target.range(),
|
||||||
);
|
);
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic.try_set_fix(|| {
|
||||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
let (import_edit, binding) =
|
||||||
&ImportRequest::import(module.as_str(), "TypeAlias"),
|
checker.import_from_typing("TypeAlias", target.start(), PythonVersion::PY310)?;
|
||||||
target.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
)?;
|
|
||||||
Ok(Fix::safe_edits(
|
Ok(Fix::safe_edits(
|
||||||
Edit::range_replacement(format!("{id}: {binding}"), target.range()),
|
Edit::range_replacement(format!("{id}: {binding}"), target.range()),
|
||||||
[import_edit],
|
[import_edit],
|
||||||
|
|
|
@ -14,10 +14,10 @@ PYI026.pyi:3:1: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.g
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 1 | from typing import Literal, Any
|
1 1 | from typing import Literal, Any
|
||||||
2 |+import typing_extensions
|
2 |+from typing_extensions import TypeAlias
|
||||||
2 3 |
|
2 3 |
|
||||||
3 |-NewAny = Any
|
3 |-NewAny = Any
|
||||||
4 |+NewAny: typing_extensions.TypeAlias = Any
|
4 |+NewAny: TypeAlias = Any
|
||||||
4 5 | OptionalStr = typing.Optional[str]
|
4 5 | OptionalStr = typing.Optional[str]
|
||||||
5 6 | Foo = Literal["foo"]
|
5 6 | Foo = Literal["foo"]
|
||||||
6 7 | IntOrStr = int | str
|
6 7 | IntOrStr = int | str
|
||||||
|
@ -34,11 +34,11 @@ PYI026.pyi:4:1: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.g
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 1 | from typing import Literal, Any
|
1 1 | from typing import Literal, Any
|
||||||
2 |+import typing_extensions
|
2 |+from typing_extensions import TypeAlias
|
||||||
2 3 |
|
2 3 |
|
||||||
3 4 | NewAny = Any
|
3 4 | NewAny = Any
|
||||||
4 |-OptionalStr = typing.Optional[str]
|
4 |-OptionalStr = typing.Optional[str]
|
||||||
5 |+OptionalStr: typing_extensions.TypeAlias = typing.Optional[str]
|
5 |+OptionalStr: TypeAlias = typing.Optional[str]
|
||||||
5 6 | Foo = Literal["foo"]
|
5 6 | Foo = Literal["foo"]
|
||||||
6 7 | IntOrStr = int | str
|
6 7 | IntOrStr = int | str
|
||||||
7 8 | AliasNone = None
|
7 8 | AliasNone = None
|
||||||
|
@ -56,12 +56,12 @@ PYI026.pyi:5:1: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.g
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 1 | from typing import Literal, Any
|
1 1 | from typing import Literal, Any
|
||||||
2 |+import typing_extensions
|
2 |+from typing_extensions import TypeAlias
|
||||||
2 3 |
|
2 3 |
|
||||||
3 4 | NewAny = Any
|
3 4 | NewAny = Any
|
||||||
4 5 | OptionalStr = typing.Optional[str]
|
4 5 | OptionalStr = typing.Optional[str]
|
||||||
5 |-Foo = Literal["foo"]
|
5 |-Foo = Literal["foo"]
|
||||||
6 |+Foo: typing_extensions.TypeAlias = Literal["foo"]
|
6 |+Foo: TypeAlias = Literal["foo"]
|
||||||
6 7 | IntOrStr = int | str
|
6 7 | IntOrStr = int | str
|
||||||
7 8 | AliasNone = None
|
7 8 | AliasNone = None
|
||||||
8 9 |
|
8 9 |
|
||||||
|
@ -78,13 +78,13 @@ PYI026.pyi:6:1: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.g
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 1 | from typing import Literal, Any
|
1 1 | from typing import Literal, Any
|
||||||
2 |+import typing_extensions
|
2 |+from typing_extensions import TypeAlias
|
||||||
2 3 |
|
2 3 |
|
||||||
3 4 | NewAny = Any
|
3 4 | NewAny = Any
|
||||||
4 5 | OptionalStr = typing.Optional[str]
|
4 5 | OptionalStr = typing.Optional[str]
|
||||||
5 6 | Foo = Literal["foo"]
|
5 6 | Foo = Literal["foo"]
|
||||||
6 |-IntOrStr = int | str
|
6 |-IntOrStr = int | str
|
||||||
7 |+IntOrStr: typing_extensions.TypeAlias = int | str
|
7 |+IntOrStr: TypeAlias = int | str
|
||||||
7 8 | AliasNone = None
|
7 8 | AliasNone = None
|
||||||
8 9 |
|
8 9 |
|
||||||
9 10 | NewAny: typing.TypeAlias = Any
|
9 10 | NewAny: typing.TypeAlias = Any
|
||||||
|
@ -102,14 +102,14 @@ PYI026.pyi:7:1: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.g
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 1 | from typing import Literal, Any
|
1 1 | from typing import Literal, Any
|
||||||
2 |+import typing_extensions
|
2 |+from typing_extensions import TypeAlias
|
||||||
2 3 |
|
2 3 |
|
||||||
3 4 | NewAny = Any
|
3 4 | NewAny = Any
|
||||||
4 5 | OptionalStr = typing.Optional[str]
|
4 5 | OptionalStr = typing.Optional[str]
|
||||||
5 6 | Foo = Literal["foo"]
|
5 6 | Foo = Literal["foo"]
|
||||||
6 7 | IntOrStr = int | str
|
6 7 | IntOrStr = int | str
|
||||||
7 |-AliasNone = None
|
7 |-AliasNone = None
|
||||||
8 |+AliasNone: typing_extensions.TypeAlias = None
|
8 |+AliasNone: TypeAlias = None
|
||||||
8 9 |
|
8 9 |
|
||||||
9 10 | NewAny: typing.TypeAlias = Any
|
9 10 | NewAny: typing.TypeAlias = Any
|
||||||
10 11 | OptionalStr: TypeAlias = typing.Optional[str]
|
10 11 | OptionalStr: TypeAlias = typing.Optional[str]
|
||||||
|
@ -126,7 +126,7 @@ PYI026.pyi:17:5: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
1 1 | from typing import Literal, Any
|
1 1 | from typing import Literal, Any
|
||||||
2 |+import typing_extensions
|
2 |+from typing_extensions import TypeAlias
|
||||||
2 3 |
|
2 3 |
|
||||||
3 4 | NewAny = Any
|
3 4 | NewAny = Any
|
||||||
4 5 | OptionalStr = typing.Optional[str]
|
4 5 | OptionalStr = typing.Optional[str]
|
||||||
|
@ -135,7 +135,7 @@ PYI026.pyi:17:5: PYI026 [*] Use `typing_extensions.TypeAlias` for type alias, e.
|
||||||
15 16 |
|
15 16 |
|
||||||
16 17 | class NotAnEnum:
|
16 17 | class NotAnEnum:
|
||||||
17 |- FLAG_THIS = None
|
17 |- FLAG_THIS = None
|
||||||
18 |+ FLAG_THIS: typing_extensions.TypeAlias = None
|
18 |+ FLAG_THIS: TypeAlias = None
|
||||||
18 19 |
|
18 19 |
|
||||||
19 20 | # these are ok
|
19 20 | # these are ok
|
||||||
20 21 | from enum import Enum
|
20 21 | from enum import Enum
|
||||||
|
|
|
@ -10,7 +10,6 @@ use ruff_python_ast::{self as ast, Expr, Operator, Parameters};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::importer::ImportRequest;
|
|
||||||
|
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
|
|
||||||
|
@ -137,11 +136,8 @@ fn generate_fix(checker: &Checker, conversion_type: ConversionType, expr: &Expr)
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
ConversionType::Optional => {
|
ConversionType::Optional => {
|
||||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
let (import_edit, binding) =
|
||||||
&ImportRequest::import_from("typing", "Optional"),
|
checker.import_from_typing("Optional", expr.start(), PythonVersion::lowest())?;
|
||||||
expr.start(),
|
|
||||||
checker.semantic(),
|
|
||||||
)?;
|
|
||||||
let new_expr = Expr::Subscript(ast::ExprSubscript {
|
let new_expr = Expr::Subscript(ast::ExprSubscript {
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
value: Box::new(Expr::Name(ast::ExprName {
|
value: Box::new(Expr::Name(ast::ExprName {
|
||||||
|
|
|
@ -44,6 +44,11 @@ impl PythonVersion {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The minimum supported Python version.
|
||||||
|
pub const fn lowest() -> Self {
|
||||||
|
Self::PY37
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn latest() -> Self {
|
pub const fn latest() -> Self {
|
||||||
Self::PY313
|
Self::PY313
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue