Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import asyncio
import collections
import contextlib
import dataclasses
import functools
import os
import queue
import re
import shelve
import types
import weakref
from collections.abc import (
AsyncGenerator,
AsyncIterable,
AsyncIterator,
Awaitable,
ByteString,
Callable,
Collection,
Container,
Coroutine,
Generator,
Iterable,
Iterator,
ItemsView,
KeysView,
Mapping,
MappingView,
MutableMapping,
MutableSequence,
MutableSet,
Reversible,
Sequence,
Set,
ValuesView,
)


def takes_preview_generics(
future: asyncio.Future[int],
task: asyncio.Task[str],
deque_object: collections.deque[int],
defaultdict_object: collections.defaultdict[str, int],
ordered_dict: collections.OrderedDict[str, int],
counter_obj: collections.Counter[str],
chain_map: collections.ChainMap[str, int],
context_manager: contextlib.AbstractContextManager[str],
async_context_manager: contextlib.AbstractAsyncContextManager[int],
dataclass_field: dataclasses.Field[int],
cached_prop: functools.cached_property[int],
partial_method: functools.partialmethod[int],
path_like: os.PathLike[str],
lifo_queue: queue.LifoQueue[int],
regular_queue: queue.Queue[int],
priority_queue: queue.PriorityQueue[int],
simple_queue: queue.SimpleQueue[int],
regex_pattern: re.Pattern[str],
regex_match: re.Match[str],
bsd_db_shelf: shelve.BsdDbShelf[str, int],
db_filename_shelf: shelve.DbfilenameShelf[str, int],
shelf_obj: shelve.Shelf[str, int],
mapping_proxy: types.MappingProxyType[str, int],
weak_key_dict: weakref.WeakKeyDictionary[object, int],
weak_method: weakref.WeakMethod[int],
weak_set: weakref.WeakSet[int],
weak_value_dict: weakref.WeakValueDictionary[object, int],
awaitable: Awaitable[int],
coroutine: Coroutine[int, None, str],
async_iterable: AsyncIterable[int],
async_iterator: AsyncIterator[int],
async_generator: AsyncGenerator[int, None],
iterable: Iterable[int],
iterator: Iterator[int],
generator: Generator[int, None, None],
reversible: Reversible[int],
container: Container[int],
collection: Collection[int],
callable_obj: Callable[[int], str],
set_obj: Set[int],
mutable_set: MutableSet[int],
mapping: Mapping[str, int],
mutable_mapping: MutableMapping[str, int],
sequence: Sequence[int],
mutable_sequence: MutableSequence[int],
byte_string: ByteString[int],
mapping_view: MappingView[str, int],
keys_view: KeysView[str],
items_view: ItemsView[str, int],
values_view: ValuesView[int],
) -> None:
...
9 changes: 7 additions & 2 deletions crates/ruff_linter/src/checkers/ast/analyze/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use ruff_text_size::Ranged;

use crate::checkers::ast::Checker;
use crate::preview::{
is_optional_as_none_in_union_enabled, is_unnecessary_default_type_args_stubs_enabled,
is_future_required_preview_generics_enabled, is_optional_as_none_in_union_enabled,
is_unnecessary_default_type_args_stubs_enabled,
};
use crate::registry::Rule;
use crate::rules::{
Expand Down Expand Up @@ -69,7 +70,11 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
&& checker.semantic.in_annotation()
&& checker.semantic.in_runtime_evaluated_annotation()
&& !checker.semantic.in_string_type_definition()
&& typing::is_pep585_generic(value, &checker.semantic)
&& typing::is_pep585_generic(
value,
&checker.semantic,
is_future_required_preview_generics_enabled(checker.settings()),
)
{
flake8_future_annotations::rules::future_required_type_annotation(
checker,
Expand Down
5 changes: 5 additions & 0 deletions crates/ruff_linter/src/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ pub(crate) const fn is_optional_as_none_in_union_enabled(settings: &LinterSettin
settings.preview.is_enabled()
}

// https://github.com/astral-sh/ruff/pull/20659
pub(crate) const fn is_future_required_preview_generics_enabled(settings: &LinterSettings) -> bool {
settings.preview.is_enabled()
}

// https://github.com/astral-sh/ruff/pull/18683
pub(crate) const fn is_safe_super_call_with_parameters_fix_enabled(
settings: &LinterSettings,
Expand Down
17 changes: 17 additions & 0 deletions crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod tests {
use test_case::test_case;

use crate::registry::Rule;
use crate::settings::types::PreviewMode;
use crate::test::test_path;
use crate::{assert_diagnostics, settings};
use ruff_python_ast::PythonVersion;
Expand Down Expand Up @@ -39,6 +40,7 @@ mod tests {
}

#[test_case(Path::new("no_future_import_uses_lowercase.py"))]
#[test_case(Path::new("no_future_import_uses_preview_generics.py"))]
#[test_case(Path::new("no_future_import_uses_union.py"))]
#[test_case(Path::new("no_future_import_uses_union_inner.py"))]
#[test_case(Path::new("ok_no_types.py"))]
Expand All @@ -56,4 +58,19 @@ mod tests {
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}

#[test_case(Path::new("no_future_import_uses_preview_generics.py"))]
fn fa102_preview(path: &Path) -> Result<()> {
let snapshot = format!("fa102_preview_{}", path.to_string_lossy());
let diagnostics = test_path(
Path::new("flake8_future_annotations").join(path).as_path(),
&settings::LinterSettings {
unresolved_target_version: PythonVersion::PY37.into(),
preview: PreviewMode::Enabled,
..settings::LinterSettings::for_rule(Rule::FutureRequiredTypeAnnotation)
},
)?;
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
---
FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
--> no_future_import_uses_preview_generics.py:42:19
|
40 | future: asyncio.Future[int],
41 | task: asyncio.Task[str],
42 | deque_object: collections.deque[int],
| ^^^^^^^^^^^^^^^^^^^^^^
43 | defaultdict_object: collections.defaultdict[str, int],
44 | ordered_dict: collections.OrderedDict[str, int],
|
help: Add `from __future__ import annotations`
1 + from __future__ import annotations
2 | import asyncio
3 | import collections
4 | import contextlib
note: This is an unsafe fix and may change runtime behavior

FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
--> no_future_import_uses_preview_generics.py:43:25
|
41 | task: asyncio.Task[str],
42 | deque_object: collections.deque[int],
43 | defaultdict_object: collections.defaultdict[str, int],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44 | ordered_dict: collections.OrderedDict[str, int],
45 | counter_obj: collections.Counter[str],
|
help: Add `from __future__ import annotations`
1 + from __future__ import annotations
2 | import asyncio
3 | import collections
4 | import contextlib
note: This is an unsafe fix and may change runtime behavior
Loading