mirror of https://github.com/astral-sh/ruff
[`pyupgrade`] Extend `UP019` to detect `typing_extensions.Text` (`UP019`) (#20825)
Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
abf685b030
commit
9e1aafd0ce
|
|
@ -18,3 +18,20 @@ def print_third_word(word: Hello.Text) -> None:
|
||||||
|
|
||||||
def print_fourth_word(word: Goodbye) -> None:
|
def print_fourth_word(word: Goodbye) -> None:
|
||||||
print(word)
|
print(word)
|
||||||
|
|
||||||
|
|
||||||
|
import typing_extensions
|
||||||
|
import typing_extensions as TypingExt
|
||||||
|
from typing_extensions import Text as TextAlias
|
||||||
|
|
||||||
|
|
||||||
|
def print_fifth_word(word: typing_extensions.Text) -> None:
|
||||||
|
print(word)
|
||||||
|
|
||||||
|
|
||||||
|
def print_sixth_word(word: TypingExt.Text) -> None:
|
||||||
|
print(word)
|
||||||
|
|
||||||
|
|
||||||
|
def print_seventh_word(word: TextAlias) -> None:
|
||||||
|
print(word)
|
||||||
|
|
|
||||||
|
|
@ -265,3 +265,7 @@ pub(crate) const fn is_fix_read_whole_file_enabled(settings: &LinterSettings) ->
|
||||||
pub(crate) const fn is_fix_write_whole_file_enabled(settings: &LinterSettings) -> bool {
|
pub(crate) const fn is_fix_write_whole_file_enabled(settings: &LinterSettings) -> bool {
|
||||||
settings.preview.is_enabled()
|
settings.preview.is_enabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_typing_extensions_str_alias_enabled(settings: &LinterSettings) -> bool {
|
||||||
|
settings.preview.is_enabled()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case(Rule::SuperCallWithParameters, Path::new("UP008.py"))]
|
#[test_case(Rule::SuperCallWithParameters, Path::new("UP008.py"))]
|
||||||
|
#[test_case(Rule::TypingTextStrAlias, Path::new("UP019.py"))]
|
||||||
fn rules_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
fn rules_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||||
let snapshot = format!("{}__preview", path.to_string_lossy());
|
let snapshot = format!("{}__preview", path.to_string_lossy());
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::Expr;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_semantic::Modules;
|
use ruff_python_semantic::Modules;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::preview::is_typing_extensions_str_alias_enabled;
|
||||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for uses of `typing.Text`.
|
/// Checks for uses of `typing.Text`.
|
||||||
///
|
///
|
||||||
|
/// In preview mode, also checks for `typing_extensions.Text`.
|
||||||
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// `typing.Text` is an alias for `str`, and only exists for Python 2
|
/// `typing.Text` is an alias for `str`, and only exists for Python 2
|
||||||
/// compatibility. As of Python 3.11, `typing.Text` is deprecated. Use `str`
|
/// compatibility. As of Python 3.11, `typing.Text` is deprecated. Use `str`
|
||||||
|
|
@ -30,14 +34,16 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: `typing.Text`](https://docs.python.org/3/library/typing.html#typing.Text)
|
/// - [Python documentation: `typing.Text`](https://docs.python.org/3/library/typing.html#typing.Text)
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
pub(crate) struct TypingTextStrAlias;
|
pub(crate) struct TypingTextStrAlias {
|
||||||
|
module: TypingModule,
|
||||||
|
}
|
||||||
|
|
||||||
impl Violation for TypingTextStrAlias {
|
impl Violation for TypingTextStrAlias {
|
||||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
"`typing.Text` is deprecated, use `str`".to_string()
|
format!("`{}.Text` is deprecated, use `str`", self.module)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fix_title(&self) -> Option<String> {
|
fn fix_title(&self) -> Option<String> {
|
||||||
|
|
@ -47,16 +53,26 @@ impl Violation for TypingTextStrAlias {
|
||||||
|
|
||||||
/// UP019
|
/// UP019
|
||||||
pub(crate) fn typing_text_str_alias(checker: &Checker, expr: &Expr) {
|
pub(crate) fn typing_text_str_alias(checker: &Checker, expr: &Expr) {
|
||||||
if !checker.semantic().seen_module(Modules::TYPING) {
|
if !checker
|
||||||
|
.semantic()
|
||||||
|
.seen_module(Modules::TYPING | Modules::TYPING_EXTENSIONS)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if checker
|
if let Some(qualified_name) = checker.semantic().resolve_qualified_name(expr) {
|
||||||
.semantic()
|
let segments = qualified_name.segments();
|
||||||
.resolve_qualified_name(expr)
|
let module = match segments {
|
||||||
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["typing", "Text"]))
|
["typing", "Text"] => TypingModule::Typing,
|
||||||
|
["typing_extensions", "Text"]
|
||||||
|
if is_typing_extensions_str_alias_enabled(checker.settings()) =>
|
||||||
{
|
{
|
||||||
let mut diagnostic = checker.report_diagnostic(TypingTextStrAlias, expr.range());
|
TypingModule::TypingExtensions
|
||||||
|
}
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut diagnostic = checker.report_diagnostic(TypingTextStrAlias { module }, expr.range());
|
||||||
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
|
diagnostic.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic.try_set_fix(|| {
|
||||||
let (import_edit, binding) = checker.importer().get_or_import_builtin_symbol(
|
let (import_edit, binding) = checker.importer().get_or_import_builtin_symbol(
|
||||||
|
|
@ -71,3 +87,18 @@ pub(crate) fn typing_text_str_alias(checker: &Checker, expr: &Expr) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum TypingModule {
|
||||||
|
Typing,
|
||||||
|
TypingExtensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TypingModule {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TypingModule::Typing => f.write_str("typing"),
|
||||||
|
TypingModule::TypingExtensions => f.write_str("typing_extensions"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,3 +66,5 @@ help: Replace with `str`
|
||||||
- def print_fourth_word(word: Goodbye) -> None:
|
- def print_fourth_word(word: Goodbye) -> None:
|
||||||
19 + def print_fourth_word(word: str) -> None:
|
19 + def print_fourth_word(word: str) -> None:
|
||||||
20 | print(word)
|
20 | print(word)
|
||||||
|
21 |
|
||||||
|
22 |
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||||
|
---
|
||||||
|
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:7:22
|
||||||
|
|
|
||||||
|
7 | def print_word(word: Text) -> None:
|
||||||
|
| ^^^^
|
||||||
|
8 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
4 | from typing import Text as Goodbye
|
||||||
|
5 |
|
||||||
|
6 |
|
||||||
|
- def print_word(word: Text) -> None:
|
||||||
|
7 + def print_word(word: str) -> None:
|
||||||
|
8 | print(word)
|
||||||
|
9 |
|
||||||
|
10 |
|
||||||
|
|
||||||
|
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:11:29
|
||||||
|
|
|
||||||
|
11 | def print_second_word(word: typing.Text) -> None:
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
12 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
8 | print(word)
|
||||||
|
9 |
|
||||||
|
10 |
|
||||||
|
- def print_second_word(word: typing.Text) -> None:
|
||||||
|
11 + def print_second_word(word: str) -> None:
|
||||||
|
12 | print(word)
|
||||||
|
13 |
|
||||||
|
14 |
|
||||||
|
|
||||||
|
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:15:28
|
||||||
|
|
|
||||||
|
15 | def print_third_word(word: Hello.Text) -> None:
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
16 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
12 | print(word)
|
||||||
|
13 |
|
||||||
|
14 |
|
||||||
|
- def print_third_word(word: Hello.Text) -> None:
|
||||||
|
15 + def print_third_word(word: str) -> None:
|
||||||
|
16 | print(word)
|
||||||
|
17 |
|
||||||
|
18 |
|
||||||
|
|
||||||
|
UP019 [*] `typing.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:19:29
|
||||||
|
|
|
||||||
|
19 | def print_fourth_word(word: Goodbye) -> None:
|
||||||
|
| ^^^^^^^
|
||||||
|
20 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
16 | print(word)
|
||||||
|
17 |
|
||||||
|
18 |
|
||||||
|
- def print_fourth_word(word: Goodbye) -> None:
|
||||||
|
19 + def print_fourth_word(word: str) -> None:
|
||||||
|
20 | print(word)
|
||||||
|
21 |
|
||||||
|
22 |
|
||||||
|
|
||||||
|
UP019 [*] `typing_extensions.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:28:28
|
||||||
|
|
|
||||||
|
28 | def print_fifth_word(word: typing_extensions.Text) -> None:
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
29 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
25 | from typing_extensions import Text as TextAlias
|
||||||
|
26 |
|
||||||
|
27 |
|
||||||
|
- def print_fifth_word(word: typing_extensions.Text) -> None:
|
||||||
|
28 + def print_fifth_word(word: str) -> None:
|
||||||
|
29 | print(word)
|
||||||
|
30 |
|
||||||
|
31 |
|
||||||
|
|
||||||
|
UP019 [*] `typing_extensions.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:32:28
|
||||||
|
|
|
||||||
|
32 | def print_sixth_word(word: TypingExt.Text) -> None:
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
33 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
29 | print(word)
|
||||||
|
30 |
|
||||||
|
31 |
|
||||||
|
- def print_sixth_word(word: TypingExt.Text) -> None:
|
||||||
|
32 + def print_sixth_word(word: str) -> None:
|
||||||
|
33 | print(word)
|
||||||
|
34 |
|
||||||
|
35 |
|
||||||
|
|
||||||
|
UP019 [*] `typing_extensions.Text` is deprecated, use `str`
|
||||||
|
--> UP019.py:36:30
|
||||||
|
|
|
||||||
|
36 | def print_seventh_word(word: TextAlias) -> None:
|
||||||
|
| ^^^^^^^^^
|
||||||
|
37 | print(word)
|
||||||
|
|
|
||||||
|
help: Replace with `str`
|
||||||
|
33 | print(word)
|
||||||
|
34 |
|
||||||
|
35 |
|
||||||
|
- def print_seventh_word(word: TextAlias) -> None:
|
||||||
|
36 + def print_seventh_word(word: str) -> None:
|
||||||
|
37 | print(word)
|
||||||
Loading…
Reference in New Issue