Respect multi-segment submodule imports when resolving qualified names (#9382)

Ensures that if the user has `import collections.abc`, then
`get_or_import_symbol` returns `collections.abc.Iterator` (or similar)
when requested.
This commit is contained in:
Charlie Marsh 2024-01-03 12:24:20 -04:00 committed by GitHub
parent 1ffc738c84
commit 7b6baff734
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 111 deletions

View file

@ -51,19 +51,14 @@ PYI058.py:21:13: PYI058 [*] Use `Iterator` as the return value for simple `__ite
= help: Convert the return annotation of your `__iter__` method to `Iterator` = help: Convert the return annotation of your `__iter__` method to `Iterator`
Safe fix Safe fix
1 |+from collections.abc import Iterator 18 18 | import collections.abc
1 2 | def scope(): 19 19 |
2 3 | from collections.abc import Generator 20 20 | class IteratorReturningSimpleGenerator3:
3 4 |
--------------------------------------------------------------------------------
18 19 | import collections.abc
19 20 |
20 21 | class IteratorReturningSimpleGenerator3:
21 |- def __iter__(self) -> collections.abc.Generator: 21 |- def __iter__(self) -> collections.abc.Generator:
22 |+ def __iter__(self) -> Iterator: 21 |+ def __iter__(self) -> collections.abc.Iterator:
22 23 | ... # PYI058 (use `Iterator`) 22 22 | ... # PYI058 (use `Iterator`)
23 24 | 23 23 |
24 25 | 24 24 |
PYI058.py:30:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods PYI058.py:30:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
| |
@ -75,19 +70,14 @@ PYI058.py:30:13: PYI058 [*] Use `Iterator` as the return value for simple `__ite
= help: Convert the return annotation of your `__iter__` method to `Iterator` = help: Convert the return annotation of your `__iter__` method to `Iterator`
Safe fix Safe fix
1 |+from collections.abc import Iterator 27 27 | from typing import Any
1 2 | def scope(): 28 28 |
2 3 | from collections.abc import Generator 29 29 | class IteratorReturningSimpleGenerator4:
3 4 |
--------------------------------------------------------------------------------
27 28 | from typing import Any
28 29 |
29 30 | class IteratorReturningSimpleGenerator4:
30 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: 30 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]:
31 |+ def __iter__(self, /) -> Iterator[str]: 30 |+ def __iter__(self, /) -> collections.abc.Iterator[str]:
31 32 | ... # PYI058 (use `Iterator`) 31 31 | ... # PYI058 (use `Iterator`)
32 33 | 32 32 |
33 34 | 33 33 |
PYI058.py:39:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods PYI058.py:39:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
| |
@ -99,19 +89,14 @@ PYI058.py:39:13: PYI058 [*] Use `Iterator` as the return value for simple `__ite
= help: Convert the return annotation of your `__iter__` method to `Iterator` = help: Convert the return annotation of your `__iter__` method to `Iterator`
Safe fix Safe fix
1 |+from collections.abc import Iterator 36 36 | import typing
1 2 | def scope(): 37 37 |
2 3 | from collections.abc import Generator 38 38 | class IteratorReturningSimpleGenerator5:
3 4 |
--------------------------------------------------------------------------------
36 37 | import typing
37 38 |
38 39 | class IteratorReturningSimpleGenerator5:
39 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: 39 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]:
40 |+ def __iter__(self, /) -> Iterator[str]: 39 |+ def __iter__(self, /) -> collections.abc.Iterator[str]:
40 41 | ... # PYI058 (use `Iterator`) 40 40 | ... # PYI058 (use `Iterator`)
41 42 | 41 41 |
42 43 | 42 42 |
PYI058.py:47:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods PYI058.py:47:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
| |
@ -167,18 +152,13 @@ PYI058.py:73:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `
= help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator`
Safe fix Safe fix
1 |+from collections.abc import AsyncIterator 70 70 | import collections.abc
1 2 | def scope(): 71 71 |
2 3 | from collections.abc import Generator 72 72 | class AsyncIteratorReturningSimpleAsyncGenerator3:
3 4 |
--------------------------------------------------------------------------------
70 71 | import collections.abc
71 72 |
72 73 | class AsyncIteratorReturningSimpleAsyncGenerator3:
73 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: 73 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]:
74 |+ def __aiter__(self, /) -> AsyncIterator[str]: 73 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]:
74 75 | ... # PYI058 (Use `AsyncIterator`) 74 74 | ... # PYI058 (Use `AsyncIterator`)
75 76 | 75 75 |
76 77 | 76 76 |

View file

@ -54,19 +54,14 @@ PYI058.pyi:17:13: PYI058 [*] Use `Iterator` as the return value for simple `__it
= help: Convert the return annotation of your `__iter__` method to `Iterator` = help: Convert the return annotation of your `__iter__` method to `Iterator`
Safe fix Safe fix
1 |+from collections.abc import Iterator 14 14 | import collections.abc
1 2 | def scope(): 15 15 |
2 3 | from collections.abc import Generator 16 16 | class IteratorReturningSimpleGenerator3:
3 4 |
--------------------------------------------------------------------------------
14 15 | import collections.abc
15 16 |
16 17 | class IteratorReturningSimpleGenerator3:
17 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) 17 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`)
18 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) 17 |+ def __iter__(self) -> collections.abc.Iterator: ... # PYI058 (use `Iterator`)
18 19 | 18 18 |
19 20 | def scope(): 19 19 | def scope():
20 21 | import collections.abc 20 20 | import collections.abc
PYI058.pyi:24:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods PYI058.pyi:24:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
| |
@ -79,19 +74,14 @@ PYI058.pyi:24:13: PYI058 [*] Use `Iterator` as the return value for simple `__it
= help: Convert the return annotation of your `__iter__` method to `Iterator` = help: Convert the return annotation of your `__iter__` method to `Iterator`
Safe fix Safe fix
1 |+from collections.abc import Iterator 21 21 | from typing import Any
1 2 | def scope(): 22 22 |
2 3 | from collections.abc import Generator 23 23 | class IteratorReturningSimpleGenerator4:
3 4 |
--------------------------------------------------------------------------------
21 22 | from typing import Any
22 23 |
23 24 | class IteratorReturningSimpleGenerator4:
24 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) 24 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`)
25 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) 24 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`)
25 26 | 25 25 |
26 27 | def scope(): 26 26 | def scope():
27 28 | import collections.abc 27 27 | import collections.abc
PYI058.pyi:31:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods PYI058.pyi:31:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
| |
@ -104,19 +94,14 @@ PYI058.pyi:31:13: PYI058 [*] Use `Iterator` as the return value for simple `__it
= help: Convert the return annotation of your `__iter__` method to `Iterator` = help: Convert the return annotation of your `__iter__` method to `Iterator`
Safe fix Safe fix
1 |+from collections.abc import Iterator 28 28 | import typing
1 2 | def scope(): 29 29 |
2 3 | from collections.abc import Generator 30 30 | class IteratorReturningSimpleGenerator5:
3 4 |
--------------------------------------------------------------------------------
28 29 | import typing
29 30 |
30 31 | class IteratorReturningSimpleGenerator5:
31 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) 31 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`)
32 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) 31 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`)
32 33 | 32 32 |
33 34 | def scope(): 33 33 | def scope():
34 35 | from collections.abc import Generator 34 34 | from collections.abc import Generator
PYI058.pyi:37:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods PYI058.pyi:37:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
| |
@ -173,19 +158,14 @@ PYI058.pyi:49:13: PYI058 [*] Use `AsyncIterator` as the return value for simple
= help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator`
Safe fix Safe fix
1 |+from collections.abc import AsyncIterator 46 46 | import collections.abc
1 2 | def scope(): 47 47 |
2 3 | from collections.abc import Generator 48 48 | class AsyncIteratorReturningSimpleAsyncGenerator3:
3 4 |
--------------------------------------------------------------------------------
46 47 | import collections.abc
47 48 |
48 49 | class AsyncIteratorReturningSimpleAsyncGenerator3:
49 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: 49 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]:
50 |+ def __aiter__(self, /) -> AsyncIterator[str]: 49 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]:
50 51 | ... # PYI058 (Use `AsyncIterator`) 50 50 | ... # PYI058 (Use `AsyncIterator`)
51 52 | 51 51 |
52 53 | def scope(): 52 52 | def scope():
PYI058.pyi:56:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods PYI058.pyi:56:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods
| |
@ -198,18 +178,13 @@ PYI058.pyi:56:13: PYI058 [*] Use `AsyncIterator` as the return value for simple
= help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator`
Safe fix Safe fix
1 |+from collections.abc import AsyncIterator 53 53 | import collections.abc
1 2 | def scope(): 54 54 |
2 3 | from collections.abc import Generator 55 55 | class AsyncIteratorReturningSimpleAsyncGenerator3:
3 4 |
--------------------------------------------------------------------------------
53 54 | import collections.abc
54 55 |
55 56 | class AsyncIteratorReturningSimpleAsyncGenerator3:
56 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) 56 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`)
57 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) 56 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`)
57 58 | 57 57 |
58 59 | def scope(): 58 58 | def scope():
59 60 | from typing import Iterator 59 59 | from typing import Iterator

View file

@ -802,8 +802,10 @@ impl<'a> SemanticModel<'a> {
} }
// Ex) Given `module="os"` and `object="name"`: // Ex) Given `module="os"` and `object="name"`:
// `import os.path ` -> `os.name` // `import os.path ` -> `os.name`
BindingKind::SubmoduleImport(SubmoduleImport { .. }) => { // Ex) Given `module="os.path"` and `object="join"`:
if name == module { // `import os.path ` -> `os.path.join`
BindingKind::SubmoduleImport(SubmoduleImport { call_path }) => {
if call_path.starts_with(&module_path) {
if let Some(source) = binding.source { if let Some(source) = binding.source {
// Verify that `os` isn't bound in an inner scope. // Verify that `os` isn't bound in an inner scope.
if self if self
@ -812,7 +814,7 @@ impl<'a> SemanticModel<'a> {
.all(|scope| !scope.has(name)) .all(|scope| !scope.has(name))
{ {
return Some(ImportedName { return Some(ImportedName {
name: format!("{name}.{member}"), name: format!("{module}.{member}"),
source, source,
range: self.nodes[source].range(), range: self.nodes[source].range(),
context: binding.context, context: binding.context,