Skip to content

Commit

Permalink
cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed Dec 9, 2024
1 parent 7032218 commit 2290240
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ruff_python_ast::{

use crate::checkers::ast::Checker;
use crate::importer::ImportRequest;
use crate::settings::types::PythonVersion;
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::helpers::map_subscript;
Expand Down Expand Up @@ -74,6 +75,10 @@ use ruff_text_size::Ranged;
/// async def __aenter__(self) -> Self: ...
/// def __iadd__(self, other: Foo) -> Self: ...
/// ```
///
/// ## Fix safety
/// This rule's fix is marked as unsafe as it changes the meaning of your type annotations.
///
/// ## References
/// - [Python documentation: `typing.Self`](https://docs.python.org/3/library/typing.html#typing.Self)
#[derive(ViolationMetadata)]
Expand Down Expand Up @@ -129,7 +134,7 @@ pub(crate) fn non_self_return_type(
};

// PEP 673 forbids the use of `typing(_extensions).Self` in metaclasses.
if analyze::class::is_metaclass(class_def, semantic).into() {
if analyze::class::is_metaclass(class_def, semantic).is_yes() {
return;
}

Expand Down Expand Up @@ -199,9 +204,7 @@ fn add_diagnostic(
stmt.identifier(),
);

if let Some(fix) = replace_with_self_fix(checker, stmt, returns, class_def) {
diagnostic.set_fix(fix);
}
diagnostic.try_set_fix(|| replace_with_self_fix(checker, stmt, returns, class_def));

checker.diagnostics.push(diagnostic);
}
Expand All @@ -211,61 +214,42 @@ fn replace_with_self_fix(
stmt: &Stmt,
returns: &Expr,
class_def: &ast::StmtClassDef,
) -> Option<Fix> {
) -> anyhow::Result<Fix> {
let semantic = checker.semantic();

let import_self = || -> Option<Edit> {
let target_version = checker.settings.target_version.as_tuple();
let source_module = if target_version >= (3, 11) {
let (self_import, self_binding) = {
let source_module = if checker.settings.target_version >= PythonVersion::Py311 {
"typing"
} else {
"typing_extensions"
};

let (importer, semantic) = (checker.importer(), checker.semantic());
let request = ImportRequest::import_from(source_module, "Self");

let (edit, ..) = importer
.get_or_import_symbol(&request, returns.start(), semantic)
.ok()?;

Some(edit)
importer.get_or_import_symbol(&request, returns.start(), semantic)?
};

let remove_first_argument_type_hint = || -> Option<Edit> {
let Stmt::FunctionDef(StmtFunctionDef { parameters, .. }) = stmt else {
return None;
};
let mut others = Vec::with_capacity(2);

let remove_first_argument_type_hint = || -> Option<Edit> {
let StmtFunctionDef { parameters, .. } = stmt.as_function_def_stmt()?;
let first = parameters.iter().next()?;
let annotation = first.annotation()?;

if !is_class_reference(semantic, annotation, &class_def.name) {
return None;
}

Some(Edit::deletion(first.name().end(), annotation.end()))
is_class_reference(semantic, annotation, &class_def.name)
.then(|| Edit::deletion(first.name().end(), annotation.end()))
};

let replace_return_type_with_self =
|| -> Edit { Edit::range_replacement("Self".to_string(), returns.range()) };

let import_self = import_self()?;
let mut others = Vec::with_capacity(2);

others.push(replace_return_type_with_self());

if let Some(edit) = remove_first_argument_type_hint() {
others.push(edit);
}
others.extend(remove_first_argument_type_hint());
others.push(Edit::range_replacement(self_binding, returns.range()));

let applicability = if might_be_generic(class_def, checker.semantic()) {
Applicability::DisplayOnly
} else {
Applicability::Unsafe
};

Some(Fix::applicable_edits(import_self, others, applicability))
Ok(Fix::applicable_edits(self_import, others, applicability))
}

/// Return true if `annotation` is either `ClassName` or `type[ClassName]`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
snapshot_kind: text
---
PYI034.py:21:9: PYI034 [*] `__new__` methods in classes like `Bad` usually return `self` at runtime
|
Expand All @@ -17,7 +16,7 @@ PYI034.py:21:9: PYI034 [*] `__new__` methods in classes like `Bad` usually retur
19 19 | object
20 20 | ): # Y040 Do not inherit from "object" explicitly, as it is redundant in Python 3
21 |- def __new__(cls, *args: Any, **kwargs: Any) -> Bad:
21 |+ def __new__(cls, *args: Any, **kwargs: Any) -> Self:
21 |+ def __new__(cls, *args: Any, **kwargs: Any) -> typing.Self:
22 22 | ... # Y034 "__new__" methods usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__new__", e.g. "def __new__(cls, *args: Any, **kwargs: Any) -> Self: ..."
23 23 |
24 24 | def __repr__(self) -> str:
Expand All @@ -37,7 +36,7 @@ PYI034.py:36:9: PYI034 [*] `__enter__` methods in classes like `Bad` usually ret
34 34 | ... # Y032 Prefer "object" to "Any" for the second parameter in "__ne__" methods
35 35 |
36 |- def __enter__(self) -> Bad:
36 |+ def __enter__(self) -> Self:
36 |+ def __enter__(self) -> typing.Self:
37 37 | ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..."
38 38 |
39 39 | async def __aenter__(self) -> Bad:
Expand All @@ -57,7 +56,7 @@ PYI034.py:39:15: PYI034 [*] `__aenter__` methods in classes like `Bad` usually r
37 37 | ... # Y034 "__enter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__enter__", e.g. "def __enter__(self) -> Self: ..."
38 38 |
39 |- async def __aenter__(self) -> Bad:
39 |+ async def __aenter__(self) -> Self:
39 |+ async def __aenter__(self) -> typing.Self:
40 40 | ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..."
41 41 |
42 42 | def __iadd__(self, other: Bad) -> Bad:
Expand All @@ -77,7 +76,7 @@ PYI034.py:42:9: PYI034 [*] `__iadd__` methods in classes like `Bad` usually retu
40 40 | ... # Y034 "__aenter__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__aenter__", e.g. "async def __aenter__(self) -> Self: ..."
41 41 |
42 |- def __iadd__(self, other: Bad) -> Bad:
42 |+ def __iadd__(self, other: Bad) -> Self:
42 |+ def __iadd__(self, other: Bad) -> typing.Self:
43 43 | ... # Y034 "__iadd__" methods in classes like "Bad" usually return "self" at runtime. Consider using "typing_extensions.Self" in "Bad.__iadd__", e.g. "def __iadd__(self, other: Bad) -> Self: ..."
44 44 |
45 45 |
Expand All @@ -96,7 +95,7 @@ PYI034.py:165:9: PYI034 [*] `__iter__` methods in classes like `BadIterator1` us
163 163 |
164 164 | class BadIterator1(Iterator[int]):
165 |- def __iter__(self) -> Iterator[int]:
165 |+ def __iter__(self) -> Self:
165 |+ def __iter__(self) -> typing.Self:
166 166 | ... # Y034 "__iter__" methods in classes like "BadIterator1" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator1.__iter__", e.g. "def __iter__(self) -> Self: ..."
167 167 |
168 168 |
Expand All @@ -116,7 +115,7 @@ PYI034.py:172:9: PYI034 [*] `__iter__` methods in classes like `BadIterator2` us
170 170 | typing.Iterator[int]
171 171 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
172 |- def __iter__(self) -> Iterator[int]:
172 |+ def __iter__(self) -> Self:
172 |+ def __iter__(self) -> typing.Self:
173 173 | ... # Y034 "__iter__" methods in classes like "BadIterator2" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator2.__iter__", e.g. "def __iter__(self) -> Self: ..."
174 174 |
175 175 |
Expand All @@ -136,7 +135,7 @@ PYI034.py:179:9: PYI034 [*] `__iter__` methods in classes like `BadIterator3` us
177 177 | typing.Iterator[int]
178 178 | ): # Y022 Use "collections.abc.Iterator[T]" instead of "typing.Iterator[T]" (PEP 585 syntax)
179 |- def __iter__(self) -> collections.abc.Iterator[int]:
179 |+ def __iter__(self) -> Self:
179 |+ def __iter__(self) -> typing.Self:
180 180 | ... # Y034 "__iter__" methods in classes like "BadIterator3" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator3.__iter__", e.g. "def __iter__(self) -> Self: ..."
181 181 |
182 182 |
Expand All @@ -156,7 +155,7 @@ PYI034.py:185:9: PYI034 [*] `__iter__` methods in classes like `BadIterator4` us
183 183 | class BadIterator4(Iterator[int]):
184 184 | # Note: *Iterable*, not *Iterator*, returned!
185 |- def __iter__(self) -> Iterable[int]:
185 |+ def __iter__(self) -> Self:
185 |+ def __iter__(self) -> typing.Self:
186 186 | ... # Y034 "__iter__" methods in classes like "BadIterator4" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadIterator4.__iter__", e.g. "def __iter__(self) -> Self: ..."
187 187 |
188 188 |
Expand All @@ -175,7 +174,7 @@ PYI034.py:195:9: PYI034 [*] `__aiter__` methods in classes like `BadAsyncIterato
193 193 |
194 194 | class BadAsyncIterator(collections.abc.AsyncIterator[str]):
195 |- def __aiter__(self) -> typing.AsyncIterator[str]:
195 |+ def __aiter__(self) -> Self:
195 |+ def __aiter__(self) -> typing.Self:
196 196 | ... # Y034 "__aiter__" methods in classes like "BadAsyncIterator" usually return "self" at runtime. Consider using "typing_extensions.Self" in "BadAsyncIterator.__aiter__", e.g. "def __aiter__(self) -> Self: ..." # Y022 Use "collections.abc.AsyncIterator[T]" instead of "typing.AsyncIterator[T]" (PEP 585 syntax)
197 197 |
198 198 | class SubclassOfBadIterator3(BadIterator3):
Expand All @@ -194,7 +193,7 @@ PYI034.py:199:9: PYI034 [*] `__iter__` methods in classes like `SubclassOfBadIte
197 197 |
198 198 | class SubclassOfBadIterator3(BadIterator3):
199 |- def __iter__(self) -> Iterator[int]: # Y034
199 |+ def __iter__(self) -> Self: # Y034
199 |+ def __iter__(self) -> typing.Self: # Y034
200 200 | ...
201 201 |
202 202 | class SubclassOfBadAsyncIterator(BadAsyncIterator):
Expand All @@ -213,7 +212,7 @@ PYI034.py:203:9: PYI034 [*] `__aiter__` methods in classes like `SubclassOfBadAs
201 201 |
202 202 | class SubclassOfBadAsyncIterator(BadAsyncIterator):
203 |- def __aiter__(self) -> collections.abc.AsyncIterator[str]: # Y034
203 |+ def __aiter__(self) -> Self: # Y034
203 |+ def __aiter__(self) -> typing.Self: # Y034
204 204 | ...
205 205 |
206 206 | class AsyncIteratorReturningAsyncIterable:
Expand All @@ -232,7 +231,7 @@ PYI034.py:327:9: PYI034 [*] `__new__` methods in classes like `NonGeneric1` usua
325 325 |
326 326 | class NonGeneric1(tuple):
327 |- def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
327 |+ def __new__(cls, *args, **kwargs) -> Self: ...
327 |+ def __new__(cls, *args, **kwargs) -> typing.Self: ...
328 328 | def __enter__(self: NonGeneric1) -> NonGeneric1: ...
329 329 |
330 330 | class NonGeneric2(tuple):
Expand All @@ -253,7 +252,7 @@ PYI034.py:328:9: PYI034 [*] `__enter__` methods in classes like `NonGeneric1` us
326 326 | class NonGeneric1(tuple):
327 327 | def __new__(cls: type[NonGeneric1], *args, **kwargs) -> NonGeneric1: ...
328 |- def __enter__(self: NonGeneric1) -> NonGeneric1: ...
328 |+ def __enter__(self) -> Self: ...
328 |+ def __enter__(self) -> typing.Self: ...
329 329 |
330 330 | class NonGeneric2(tuple):
331 331 | def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
Expand All @@ -273,7 +272,7 @@ PYI034.py:331:9: PYI034 [*] `__new__` methods in classes like `NonGeneric2` usua
329 329 |
330 330 | class NonGeneric2(tuple):
331 |- def __new__(cls: Type[NonGeneric2]) -> NonGeneric2: ...
331 |+ def __new__(cls) -> Self: ...
331 |+ def __new__(cls) -> typing.Self: ...
332 332 |
333 333 | class Generic1[T](list):
334 334 | def __new__(cls: type[Generic1]) -> Generic1: ...
Expand All @@ -292,7 +291,7 @@ PYI034.py:334:9: PYI034 `__new__` methods in classes like `Generic1` usually ret
332 332 |
333 333 | class Generic1[T](list):
334 |- def __new__(cls: type[Generic1]) -> Generic1: ...
334 |+ def __new__(cls) -> Self: ...
334 |+ def __new__(cls) -> typing.Self: ...
335 335 | def __enter__(self: Generic1) -> Generic1: ...
336 336 |
337 337 |
Expand All @@ -311,7 +310,7 @@ PYI034.py:335:9: PYI034 `__enter__` methods in classes like `Generic1` usually r
333 333 | class Generic1[T](list):
334 334 | def __new__(cls: type[Generic1]) -> Generic1: ...
335 |- def __enter__(self: Generic1) -> Generic1: ...
335 |+ def __enter__(self) -> Self: ...
335 |+ def __enter__(self) -> typing.Self: ...
336 336 |
337 337 |
338 338 | ### Correctness of typevar-likes are not verified.
Expand All @@ -330,7 +329,7 @@ PYI034.py:345:9: PYI034 `__new__` methods in classes like `Generic2` usually ret
343 343 |
344 344 | class Generic2(Generic[T]):
345 |- def __new__(cls: type[Generic2]) -> Generic2: ...
345 |+ def __new__(cls) -> Self: ...
345 |+ def __new__(cls) -> typing.Self: ...
346 346 | def __enter__(self: Generic2) -> Generic2: ...
347 347 |
348 348 | class Generic3(tuple[*Ts]):
Expand All @@ -351,7 +350,7 @@ PYI034.py:346:9: PYI034 `__enter__` methods in classes like `Generic2` usually r
344 344 | class Generic2(Generic[T]):
345 345 | def __new__(cls: type[Generic2]) -> Generic2: ...
346 |- def __enter__(self: Generic2) -> Generic2: ...
346 |+ def __enter__(self) -> Self: ...
346 |+ def __enter__(self) -> typing.Self: ...
347 347 |
348 348 | class Generic3(tuple[*Ts]):
349 349 | def __new__(cls: type[Generic3]) -> Generic3: ...
Expand All @@ -370,7 +369,7 @@ PYI034.py:349:9: PYI034 `__new__` methods in classes like `Generic3` usually ret
347 347 |
348 348 | class Generic3(tuple[*Ts]):
349 |- def __new__(cls: type[Generic3]) -> Generic3: ...
349 |+ def __new__(cls) -> Self: ...
349 |+ def __new__(cls) -> typing.Self: ...
350 350 | def __enter__(self: Generic3) -> Generic3: ...
351 351 |
352 352 | class Generic4(collections.abc.Callable[P, ...]):
Expand All @@ -391,7 +390,7 @@ PYI034.py:350:9: PYI034 `__enter__` methods in classes like `Generic3` usually r
348 348 | class Generic3(tuple[*Ts]):
349 349 | def __new__(cls: type[Generic3]) -> Generic3: ...
350 |- def __enter__(self: Generic3) -> Generic3: ...
350 |+ def __enter__(self) -> Self: ...
350 |+ def __enter__(self) -> typing.Self: ...
351 351 |
352 352 | class Generic4(collections.abc.Callable[P, ...]):
353 353 | def __new__(cls: type[Generic4]) -> Generic4: ...
Expand All @@ -410,7 +409,7 @@ PYI034.py:353:9: PYI034 `__new__` methods in classes like `Generic4` usually ret
351 351 |
352 352 | class Generic4(collections.abc.Callable[P, ...]):
353 |- def __new__(cls: type[Generic4]) -> Generic4: ...
353 |+ def __new__(cls) -> Self: ...
353 |+ def __new__(cls) -> typing.Self: ...
354 354 | def __enter__(self: Generic4) -> Generic4: ...
355 355 |
356 356 | from some_module import PotentialTypeVar
Expand All @@ -431,7 +430,7 @@ PYI034.py:354:9: PYI034 `__enter__` methods in classes like `Generic4` usually r
352 352 | class Generic4(collections.abc.Callable[P, ...]):
353 353 | def __new__(cls: type[Generic4]) -> Generic4: ...
354 |- def __enter__(self: Generic4) -> Generic4: ...
354 |+ def __enter__(self) -> Self: ...
354 |+ def __enter__(self) -> typing.Self: ...
355 355 |
356 356 | from some_module import PotentialTypeVar
357 357 |
Expand All @@ -450,7 +449,7 @@ PYI034.py:359:9: PYI034 [*] `__new__` methods in classes like `Generic5` usually
357 357 |
358 358 | class Generic5(list[PotentialTypeVar]):
359 |- def __new__(cls: type[Generic5]) -> Generic5: ...
359 |+ def __new__(cls) -> Self: ...
359 |+ def __new__(cls) -> typing.Self: ...
360 360 | def __enter__(self: Generic5) -> Generic5: ...
361 361 |

Expand All @@ -468,5 +467,5 @@ PYI034.py:360:9: PYI034 [*] `__enter__` methods in classes like `Generic5` usual
358 358 | class Generic5(list[PotentialTypeVar]):
359 359 | def __new__(cls: type[Generic5]) -> Generic5: ...
360 |- def __enter__(self: Generic5) -> Generic5: ...
360 |+ def __enter__(self) -> Self: ...
360 |+ def __enter__(self) -> typing.Self: ...
361 361 |
Loading

0 comments on commit 2290240

Please sign in to comment.