Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct the interface for SimplePath #480

Merged
merged 5 commits into from
Dec 16, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ API Reference
:members:
:undoc-members:
:show-inheritance:

.. automodule:: importlib_metadata._meta
:members:
:undoc-members:
:show-inheritance:
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@
('py:class', '_T'),
# Other workarounds
('py:class', 'importlib_metadata.DeprecatedNonAbstract'),
# Workaround needed for #480
('py:obj', 'importlib_metadata._meta._T'),
]
51 changes: 37 additions & 14 deletions importlib_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,14 +315,12 @@ class PackagePath(pathlib.PurePosixPath):
dist: "Distribution"

def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override]
with self.locate().open(encoding=encoding) as stream:
return stream.read()
return self.locate().read_text(encoding=encoding)

def read_binary(self) -> bytes:
with self.locate().open('rb') as stream:
return stream.read()
return self.locate().read_bytes()

def locate(self) -> pathlib.Path:
def locate(self) -> SimplePath:
"""Return a path-like object for this path"""
return self.dist.locate_file(self)

Expand All @@ -336,6 +334,7 @@ def __repr__(self) -> str:


class DeprecatedNonAbstract:
# Required until Python 3.14
def __new__(cls, *args, **kwargs):
all_names = {
name for subclass in inspect.getmro(cls) for name in vars(subclass)
Expand All @@ -355,20 +354,42 @@ def __new__(cls, *args, **kwargs):


class Distribution(DeprecatedNonAbstract):
"""A Python distribution package."""
"""
An abstract Python distribution package.

Custom providers may derive from this class and define
the abstract methods to provide a concrete implementation
for their environment.
"""

@abc.abstractmethod
def read_text(self, filename) -> Optional[str]:
"""Attempt to load metadata file given by the name.

Python distribution metadata is organized by blobs of text
typically represented as "files" in the metadata directory
(e.g. package-1.0.dist-info). These files include things
like:

- METADATA: The distribution metadata including fields
like Name and Version and Description.
- entry_points.txt: A series of entry points defined by
the Setuptools spec in an ini format with sections
representing the groups.
- RECORD: A record of files as installed by a typical
installer.

A package may provide any set of files, including those
not listed here or none at all.

:param filename: The name of the file in the distribution info.
:return: The text if found, otherwise None.
"""

@abc.abstractmethod
def locate_file(self, path: StrPath) -> pathlib.Path:
def locate_file(self, path: StrPath) -> SimplePath:
"""
Given a path to a file in this distribution, return a path
Given a path to a file in this distribution, return a SimplePath
to it.
"""

Expand All @@ -391,16 +412,18 @@ def from_name(cls, name: str) -> "Distribution":
raise PackageNotFoundError(name)

@classmethod
def discover(cls, **kwargs) -> Iterable["Distribution"]:
def discover(
cls, *, context: Optional['DistributionFinder.Context'] = None, **kwargs
) -> Iterable["Distribution"]:
"""Return an iterable of Distribution objects for all packages.

Pass a ``context`` or pass keyword arguments for constructing
a context.

:context: A ``DistributionFinder.Context`` object.
:return: Iterable of Distribution objects for all packages.
:return: Iterable of Distribution objects for packages matching
the context.
"""
context = kwargs.pop('context', None)
if context and kwargs:
raise ValueError("cannot accept context and kwargs")
context = context or DistributionFinder.Context(**kwargs)
Expand All @@ -410,7 +433,7 @@ def discover(cls, **kwargs) -> Iterable["Distribution"]:

@staticmethod
def at(path: StrPath) -> "Distribution":
"""Return a Distribution for the indicated metadata path
"""Return a Distribution for the indicated metadata path.

:param path: a string or path-like object
:return: a concrete Distribution instance for the path
Expand All @@ -419,7 +442,7 @@ def at(path: StrPath) -> "Distribution":

@staticmethod
def _discover_resolvers():
"""Search the meta_path for resolvers."""
"""Search the meta_path for resolvers (MetadataPathFinders)."""
declared = (
getattr(finder, 'find_distributions', None) for finder in sys.meta_path
)
Expand Down Expand Up @@ -831,7 +854,7 @@ def read_text(self, filename: StrPath) -> Optional[str]:

read_text.__doc__ = Distribution.read_text.__doc__

def locate_file(self, path: StrPath) -> pathlib.Path:
def locate_file(self, path: StrPath) -> SimplePath:
return self._path.parent / path

@property
Expand Down
10 changes: 8 additions & 2 deletions importlib_metadata/_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def json(self) -> Dict[str, Union[str, List[str]]]:

class SimplePath(Protocol[_T]):
"""
A minimal subset of pathlib.Path required by PathDistribution.
A minimal subset of pathlib.Path required by Distribution.
"""

def joinpath(self, other: Union[str, _T]) -> _T:
Expand All @@ -59,5 +59,11 @@ def __truediv__(self, other: Union[str, _T]) -> _T:
def parent(self) -> _T:
... # pragma: no cover

def read_text(self) -> str:
def read_text(self, encoding=None) -> str:
... # pragma: no cover

def read_bytes(self) -> bytes:
... # pragma: no cover

def exists(self) -> bool:
... # pragma: no cover
1 change: 1 addition & 0 deletions newsfragments/+b15724f6.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Corrected the interface for SimplePath to encompass the expectations of locate_file and PackagePath.