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
2 changes: 2 additions & 0 deletions stdlib/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ importlib.readers: 3.10-
importlib.resources: 3.7-
importlib.resources.abc: 3.11-
importlib.resources.readers: 3.11-
importlib.resources.simple: 3.11-
importlib.simple: 3.11-
inspect: 2.7-
io: 2.7-
ipaddress: 3.3-
Expand Down
11 changes: 9 additions & 2 deletions stdlib/importlib/readers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a matter for this PR, but at runtime (at least on main) this file re-exports code from importlib.resources.readers, so ideally we should do the same in our stub.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, see the comments on lines 1-3 of this file :)

import pathlib
import sys
import zipfile
from _typeshed import Incomplete, StrPath
from collections.abc import Iterable, Iterator
from io import BufferedReader
Expand Down Expand Up @@ -36,7 +37,7 @@ if sys.version_info >= (3, 10):
def __init__(self, loader, module: str) -> None: ...
def open_resource(self, resource: str) -> BufferedReader: ...
def is_resource(self, path: StrPath) -> bool: ...
def files(self): ...
def files(self) -> zipfile.Path: ...

class MultiplexedPath(abc.Traversable):
def __init__(self, *paths: abc.Traversable) -> None: ...
Expand All @@ -45,11 +46,17 @@ if sys.version_info >= (3, 10):
def read_text(self, *args: Never, **kwargs: Never) -> NoReturn: ... # type: ignore[override]
def is_dir(self) -> Literal[True]: ...
def is_file(self) -> Literal[False]: ...

if sys.version_info >= (3, 12):
def joinpath(self, *descendants: str) -> abc.Traversable: ...
else:
elif sys.version_info >= (3, 11):
def joinpath(self, child: str) -> abc.Traversable: ... # type: ignore[override]
else:
def joinpath(self, child: str) -> abc.Traversable: ...

if sys.version_info < (3, 12):
__truediv__ = joinpath

def open(self, *args: Never, **kwargs: Never) -> NoReturn: ... # type: ignore[override]
@property
def name(self) -> str: ...
Expand Down
49 changes: 49 additions & 0 deletions stdlib/importlib/resources/simple.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import abc
import sys
from _typeshed import Incomplete, OpenBinaryMode, OpenTextMode, Unused
from collections.abc import Iterator
from io import TextIOWrapper
from typing import IO, Any, BinaryIO, NoReturn, overload
from typing_extensions import Literal, Never

if sys.version_info >= (3, 11):
from .abc import Traversable, TraversableResources

class SimpleReader(abc.ABC):
@property
@abc.abstractmethod
def package(self) -> str: ...
@abc.abstractmethod
def children(self) -> list[SimpleReader]: ...
@abc.abstractmethod
def resources(self) -> list[str]: ...
@abc.abstractmethod
def open_binary(self, resource: str) -> BinaryIO: ...
@property
def name(self) -> str: ...

class ResourceHandle(Traversable, metaclass=abc.ABCMeta):
parent: ResourceContainer
def __init__(self, parent: ResourceContainer, name: str) -> None: ...
def is_file(self) -> Literal[True]: ...
def is_dir(self) -> Literal[False]: ...
@overload
def open(self, mode: OpenTextMode = "r", *args: Incomplete, **kwargs: Incomplete) -> TextIOWrapper: ...
@overload
def open(self, mode: OpenBinaryMode, *args: Unused, **kwargs: Unused) -> BinaryIO: ...
@overload
def open(self, mode: str, *args: Incomplete, **kwargs: Incomplete) -> IO[Any]: ...
def joinpath(self, name: Never) -> NoReturn: ... # type: ignore[override]

class ResourceContainer(Traversable, metaclass=abc.ABCMeta):
reader: SimpleReader
def __init__(self, reader: SimpleReader) -> None: ...
def is_dir(self) -> Literal[True]: ...
def is_file(self) -> Literal[False]: ...
def iterdir(self) -> Iterator[ResourceHandle | ResourceContainer]: ...
def open(self, *args: Never, **kwargs: Never) -> NoReturn: ... # type: ignore[override]
if sys.version_info < (3, 12):
def joinpath(self, *descendants: str) -> Traversable: ...

class TraversableReader(TraversableResources, SimpleReader, metaclass=abc.ABCMeta):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the metaclass? Doesn't seem to exist at runtime.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It inherits from a class that has abstract methods, but does not implement those abstract methods, meaning it is still abstract. Because it's really easy to do that accidentally in a stub, mypy has an undocumented feature that says you have to explicitly redeclare the metaclass if that's really what you want: https://github.com/python/mypy/blob/9011ca8b4dedc0e7177737b5265f69694afa91b5/mypy/semanal_classprop.py#L94

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, I implemented that feature :)

def files(self) -> ResourceContainer: ...
11 changes: 11 additions & 0 deletions stdlib/importlib/simple.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sys

if sys.version_info >= (3, 11):
from .resources.simple import (
ResourceContainer as ResourceContainer,
ResourceHandle as ResourceHandle,
SimpleReader as SimpleReader,
TraversableReader as TraversableReader,
)

__all__ = ["SimpleReader", "ResourceHandle", "ResourceContainer", "TraversableReader"]
4 changes: 0 additions & 4 deletions tests/stubtest_allowlists/py311.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ tkinter._VersionInfoType.__doc__
typing.NewType.__call__
typing.NewType.__mro_entries__

# Modules that exist at runtime, but are missing from typeshed
importlib.resources.simple
importlib.simple

# Modules that exist at runtime, but shouldn't be added to typeshed
ctypes.test
ctypes\.test\..+
Expand Down
2 changes: 0 additions & 2 deletions tests/stubtest_allowlists/py312.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Modules that exist at runtime, but are missing from typeshed
zipfile._path.glob
importlib.resources.simple
importlib.simple

# Errors that also existed on Python 3.11
_collections_abc.AsyncIterable.__class_getitem__
Expand Down