Skip to content

Commit b42e3b2

Browse files
authored
Use protocols instead of importlib.abc.Loader/MetaPathFinder/PathEntryFinder (#11890)
1 parent 6565e8a commit b42e3b2

File tree

8 files changed

+72
-25
lines changed

8 files changed

+72
-25
lines changed

stdlib/@tests/test_cases/check_importlib.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
from __future__ import annotations
2+
13
import importlib.abc
4+
import importlib.util
25
import pathlib
36
import sys
47
import zipfile
8+
from collections.abc import Sequence
9+
from importlib.machinery import ModuleSpec
10+
from types import ModuleType
11+
from typing_extensions import Self
512

613
# Assert that some Path classes are Traversable.
714
if sys.version_info >= (3, 9):
@@ -11,3 +18,30 @@ def traverse(t: importlib.abc.Traversable) -> None:
1118

1219
traverse(pathlib.Path())
1320
traverse(zipfile.Path(""))
21+
22+
23+
class MetaFinder:
24+
@classmethod
25+
def find_spec(cls, fullname: str, path: Sequence[str] | None, target: ModuleType | None = None) -> ModuleSpec | None:
26+
return None # simplified mock for demonstration purposes only
27+
28+
29+
class PathFinder:
30+
@classmethod
31+
def path_hook(cls, path_entry: str) -> type[Self]:
32+
return cls # simplified mock for demonstration purposes only
33+
34+
@classmethod
35+
def find_spec(cls, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None:
36+
return None # simplified mock for demonstration purposes only
37+
38+
39+
class Loader:
40+
@classmethod
41+
def load_module(cls, fullname: str) -> ModuleType:
42+
return ModuleType(fullname)
43+
44+
45+
sys.meta_path.append(MetaFinder)
46+
sys.path_hooks.append(PathFinder.path_hook)
47+
importlib.util.spec_from_loader("xxxx42xxxx", Loader)

stdlib/_typeshed/importlib.pyi

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Implicit protocols used in importlib.
2+
# We intentionally omit deprecated and optional methods.
3+
4+
from collections.abc import Sequence
5+
from importlib.machinery import ModuleSpec
6+
from types import ModuleType
7+
from typing import Protocol
8+
9+
__all__ = ["LoaderProtocol", "MetaPathFinderProtocol", "PathEntryFinderProtocol"]
10+
11+
class LoaderProtocol(Protocol):
12+
def load_module(self, fullname: str, /) -> ModuleType: ...
13+
14+
class MetaPathFinderProtocol(Protocol):
15+
def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ..., /) -> ModuleSpec | None: ...
16+
17+
class PathEntryFinderProtocol(Protocol):
18+
def find_spec(self, fullname: str, target: ModuleType | None = ..., /) -> ModuleSpec | None: ...

stdlib/importlib/abc.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class SourceLoader(ResourceLoader, ExecutionLoader, metaclass=ABCMeta):
6464

6565
# The base classes differ starting in 3.10:
6666
if sys.version_info >= (3, 10):
67-
# Please keep in sync with sys._MetaPathFinder
67+
# Please keep in sync with _typeshed.importlib.MetaPathFinderProtocol
6868
class MetaPathFinder(metaclass=ABCMeta):
6969
if sys.version_info < (3, 12):
7070
def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ...
@@ -85,7 +85,7 @@ if sys.version_info >= (3, 10):
8585
def find_spec(self, fullname: str, target: types.ModuleType | None = ...) -> ModuleSpec | None: ...
8686

8787
else:
88-
# Please keep in sync with sys._MetaPathFinder
88+
# Please keep in sync with _typeshed.importlib.MetaPathFinderProtocol
8989
class MetaPathFinder(Finder):
9090
def find_module(self, fullname: str, path: Sequence[str] | None) -> Loader | None: ...
9191
def invalidate_caches(self) -> None: ...

stdlib/importlib/util.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import importlib.machinery
33
import sys
44
import types
55
from _typeshed import ReadableBuffer, StrOrBytesPath
6+
from _typeshed.importlib import LoaderProtocol
67
from collections.abc import Callable
78
from typing import Any
89
from typing_extensions import ParamSpec
@@ -23,13 +24,13 @@ def source_from_cache(path: str) -> str: ...
2324
def decode_source(source_bytes: ReadableBuffer) -> str: ...
2425
def find_spec(name: str, package: str | None = None) -> importlib.machinery.ModuleSpec | None: ...
2526
def spec_from_loader(
26-
name: str, loader: importlib.abc.Loader | None, *, origin: str | None = None, is_package: bool | None = None
27+
name: str, loader: LoaderProtocol | None, *, origin: str | None = None, is_package: bool | None = None
2728
) -> importlib.machinery.ModuleSpec | None: ...
2829
def spec_from_file_location(
2930
name: str,
3031
location: StrOrBytesPath | None = None,
3132
*,
32-
loader: importlib.abc.Loader | None = None,
33+
loader: LoaderProtocol | None = None,
3334
submodule_search_locations: list[str] | None = ...,
3435
) -> importlib.machinery.ModuleSpec | None: ...
3536
def module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType: ...

stdlib/pkgutil.pyi

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import sys
22
from _typeshed import SupportsRead
3+
from _typeshed.importlib import LoaderProtocol, MetaPathFinderProtocol, PathEntryFinderProtocol
34
from collections.abc import Callable, Iterable, Iterator
4-
from importlib.abc import Loader, MetaPathFinder, PathEntryFinder
55
from typing import IO, Any, NamedTuple, TypeVar
66
from typing_extensions import deprecated
77

@@ -23,7 +23,7 @@ if sys.version_info < (3, 12):
2323
_PathT = TypeVar("_PathT", bound=Iterable[str])
2424

2525
class ModuleInfo(NamedTuple):
26-
module_finder: MetaPathFinder | PathEntryFinder
26+
module_finder: MetaPathFinderProtocol | PathEntryFinderProtocol
2727
name: str
2828
ispkg: bool
2929

@@ -37,11 +37,11 @@ if sys.version_info < (3, 12):
3737
def __init__(self, fullname: str, file: IO[str], filename: str, etc: tuple[str, str, int]) -> None: ...
3838

3939
@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
40-
def find_loader(fullname: str) -> Loader | None: ...
41-
def get_importer(path_item: str) -> PathEntryFinder | None: ...
40+
def find_loader(fullname: str) -> LoaderProtocol | None: ...
41+
def get_importer(path_item: str) -> PathEntryFinderProtocol | None: ...
4242
@deprecated("Use importlib.util.find_spec() instead. Will be removed in Python 3.14.")
43-
def get_loader(module_or_name: str) -> Loader | None: ...
44-
def iter_importers(fullname: str = "") -> Iterator[MetaPathFinder | PathEntryFinder]: ...
43+
def get_loader(module_or_name: str) -> LoaderProtocol | None: ...
44+
def iter_importers(fullname: str = "") -> Iterator[MetaPathFinderProtocol | PathEntryFinderProtocol]: ...
4545
def iter_modules(path: Iterable[str] | None = None, prefix: str = "") -> Iterator[ModuleInfo]: ...
4646
def read_code(stream: SupportsRead[bytes]) -> Any: ... # undocumented
4747
def walk_packages(

stdlib/sys/__init__.pyi

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import sys
22
from _typeshed import OptExcInfo, ProfileFunction, TraceFunction, structseq
3+
from _typeshed.importlib import MetaPathFinderProtocol, PathEntryFinderProtocol
34
from builtins import object as _object
45
from collections.abc import AsyncGenerator, Callable, Sequence
5-
from importlib.abc import PathEntryFinder
6-
from importlib.machinery import ModuleSpec
76
from io import TextIOWrapper
87
from types import FrameType, ModuleType, TracebackType
98
from typing import Any, Final, Literal, NoReturn, Protocol, TextIO, TypeVar, final
@@ -15,10 +14,6 @@ _T = TypeVar("_T")
1514
_ExitCode: TypeAlias = str | int | None
1615
_OptExcInfo: TypeAlias = OptExcInfo # noqa: Y047 # TODO: obsolete, remove fall 2022 or later
1716

18-
# Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder`
19-
class _MetaPathFinder(Protocol):
20-
def find_spec(self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = ..., /) -> ModuleSpec | None: ...
21-
2217
# ----- sys variables -----
2318
if sys.platform != "win32":
2419
abiflags: str
@@ -44,13 +39,13 @@ if sys.version_info >= (3, 12):
4439
last_exc: BaseException # or undefined.
4540
maxsize: int
4641
maxunicode: int
47-
meta_path: list[_MetaPathFinder]
42+
meta_path: list[MetaPathFinderProtocol]
4843
modules: dict[str, ModuleType]
4944
if sys.version_info >= (3, 10):
5045
orig_argv: list[str]
5146
path: list[str]
52-
path_hooks: list[Callable[[str], PathEntryFinder]]
53-
path_importer_cache: dict[str, PathEntryFinder | None]
47+
path_hooks: list[Callable[[str], PathEntryFinderProtocol]]
48+
path_importer_cache: dict[str, PathEntryFinderProtocol | None]
5449
platform: str
5550
if sys.version_info >= (3, 9):
5651
platlibdir: str

stdlib/types.pyi

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
from _typeshed import SupportsKeysAndGetItem
3+
from _typeshed.importlib import LoaderProtocol
34
from collections.abc import (
45
AsyncGenerator,
56
Awaitable,
@@ -16,7 +17,7 @@ from collections.abc import (
1617
from importlib.machinery import ModuleSpec
1718

1819
# pytype crashes if types.MappingProxyType inherits from collections.abc.Mapping instead of typing.Mapping
19-
from typing import Any, ClassVar, Literal, Mapping, Protocol, TypeVar, final, overload # noqa: Y022
20+
from typing import Any, ClassVar, Literal, Mapping, TypeVar, final, overload # noqa: Y022
2021
from typing_extensions import ParamSpec, Self, TypeVarTuple, deprecated
2122

2223
__all__ = [
@@ -318,15 +319,12 @@ class SimpleNamespace:
318319
def __setattr__(self, name: str, value: Any, /) -> None: ...
319320
def __delattr__(self, name: str, /) -> None: ...
320321

321-
class _LoaderProtocol(Protocol):
322-
def load_module(self, fullname: str, /) -> ModuleType: ...
323-
324322
class ModuleType:
325323
__name__: str
326324
__file__: str | None
327325
@property
328326
def __dict__(self) -> dict[str, Any]: ... # type: ignore[override]
329-
__loader__: _LoaderProtocol | None
327+
__loader__: LoaderProtocol | None
330328
__package__: str | None
331329
__path__: MutableSequence[str]
332330
__spec__: ModuleSpec | None

stubs/setuptools/pkg_resources/__init__.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import types
22
import zipimport
33
from _typeshed import Incomplete, StrPath, Unused
4+
from _typeshed.importlib import LoaderProtocol
45
from collections.abc import Callable, Generator, Iterable, Iterator, Sequence
56
from io import BytesIO
67
from itertools import chain
@@ -359,7 +360,7 @@ def evaluate_marker(text: str, extra: Incomplete | None = None) -> bool: ...
359360
class NullProvider:
360361
egg_name: str | None
361362
egg_info: str | None
362-
loader: types._LoaderProtocol | None
363+
loader: LoaderProtocol | None
363364
module_path: str | None
364365

365366
def __init__(self, module: _ModuleLike) -> None: ...

0 commit comments

Comments
 (0)