Repository navigation
Add rule for context manager iterator annotations (UP052) - #29000
Conversation
|
| code | total | + violation | - violation | + fix | - fix |
|---|---|---|---|---|---|
| context-manager-generator | 341 | 341 | 0 | 0 | 0 |
| UP052 | 1 | 1 | 0 | 0 | 0 |
| /// @contextmanager | ||
| /// def example() -> Generator[int, None, None]: | ||
| /// yield 1 |
There was a problem hiding this comment.
Do we/should we generate the shorter Generator[int] form on 3.13+? It also seems okay to leave it to unnecessary-default-type-args (UP043), though.
| let return_type = if visitor.returns_value { | ||
| "object" | ||
| } else { | ||
| "None" | ||
| }; | ||
| let (object_edit, return_type) = if visitor.returns_value { | ||
| checker.importer().get_or_import_builtin_symbol( | ||
| return_type, | ||
| function.start(), | ||
| checker.semantic(), | ||
| )? | ||
| } else { | ||
| (None, return_type.to_string()) | ||
| }; |
There was a problem hiding this comment.
This first return_type binding seems kind of redundant, if I understand correctly:
| let return_type = if visitor.returns_value { | |
| "object" | |
| } else { | |
| "None" | |
| }; | |
| let (object_edit, return_type) = if visitor.returns_value { | |
| checker.importer().get_or_import_builtin_symbol( | |
| return_type, | |
| function.start(), | |
| checker.semantic(), | |
| )? | |
| } else { | |
| (None, return_type.to_string()) | |
| }; | |
| let (object_edit, return_type) = if visitor.returns_value { | |
| checker.importer().get_or_import_builtin_symbol( | |
| "object", | |
| function.start(), | |
| checker.semantic(), | |
| )? | |
| } else { | |
| (None, "None".to_string()) | |
| }; |
| /// - [Python documentation: `contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager) | ||
| #[derive(ViolationMetadata)] | ||
| #[violation_metadata(preview_since = "NEXT_RUFF_VERSION", category = Category::Suspicious)] | ||
| pub(crate) struct ContextManagerGenerator { |
There was a problem hiding this comment.
nit: Should this be ContextManagerIterator since that's the issue being flagged in the initial code?
| } | ||
|
|
||
| #[test] | ||
| fn context_manager_generator_fixes() -> Result<()> { |
There was a problem hiding this comment.
Could we just use a regular snapshot here? Or maybe mdtests?
108e3dc to
0fa1944
Compare
Summary
The
contextmanagerandasynccontextmanagerdecorators require generators, which support methods not guaranteed by theIteratorandAsyncIteratorprotocols. Typeshed deprecates the overloads that accept these iterator annotations.Add the preview rule
context-manager-iterator(UP052) to identify these annotations and offer a fix. For example:The fix replaces
Iterator[int]withGenerator[int, None, None]. For an async context manager, it replacesAsyncIterator[int]withAsyncGenerator[int, None]. On Python 3.13 and later, it omits the default type arguments and usesGenerator[int]orAsyncGenerator[int]. If a synchronous generator returns a value, the fix usesobjectas its return type.The fix is unsafe because it changes runtime annotations and may introduce an import. The rule checks functions whose innermost decorator is a context manager and whose body contains a
yieldoryield from.