Skip to content

Commit 705a757

Browse files
committed
Merge pull request #449 from dimbleby/type-annotations
2 parents 512a3df + 2e78162 commit 705a757

File tree

5 files changed

+70
-43
lines changed

5 files changed

+70
-43
lines changed

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
v6.6.0
2+
======
3+
4+
* #449: Expanded type annotations.
5+
16
v6.5.1
27
======
38

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
nitpick_ignore = [
6565
# Workaround for #316
6666
('py:class', 'importlib_metadata.EntryPoints'),
67+
('py:class', 'importlib_metadata.PackagePath'),
6768
('py:class', 'importlib_metadata.SelectableGroups'),
6869
('py:class', 'importlib_metadata._meta._T'),
6970
# Workaround for #435

importlib_metadata/__init__.py

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import zipp
77
import email
8+
import inspect
89
import pathlib
910
import operator
1011
import textwrap
@@ -14,12 +15,12 @@
1415
import posixpath
1516
import contextlib
1617
import collections
17-
import inspect
1818

1919
from . import _adapters, _meta, _py39compat
2020
from ._collections import FreezableDefaultDict, Pair
2121
from ._compat import (
2222
NullFinder,
23+
StrPath,
2324
install,
2425
pypy_partial,
2526
)
@@ -31,8 +32,7 @@
3132
from importlib import import_module
3233
from importlib.abc import MetaPathFinder
3334
from itertools import starmap
34-
from typing import List, Mapping, Optional, cast
35-
35+
from typing import Iterable, List, Mapping, Optional, Set, cast
3636

3737
__all__ = [
3838
'Distribution',
@@ -53,11 +53,11 @@
5353
class PackageNotFoundError(ModuleNotFoundError):
5454
"""The package was not found."""
5555

56-
def __str__(self):
56+
def __str__(self) -> str:
5757
return f"No package metadata was found for {self.name}"
5858

5959
@property
60-
def name(self):
60+
def name(self) -> str: # type: ignore[override]
6161
(name,) = self.args
6262
return name
6363

@@ -123,7 +123,7 @@ def read(text, filter_=None):
123123
yield Pair(name, value)
124124

125125
@staticmethod
126-
def valid(line):
126+
def valid(line: str):
127127
return line and not line.startswith('#')
128128

129129

@@ -198,7 +198,7 @@ class EntryPoint(DeprecatedTuple):
198198

199199
dist: Optional['Distribution'] = None
200200

201-
def __init__(self, name, value, group):
201+
def __init__(self, name: str, value: str, group: str) -> None:
202202
vars(self).update(name=name, value=value, group=group)
203203

204204
def load(self):
@@ -212,18 +212,21 @@ def load(self):
212212
return functools.reduce(getattr, attrs, module)
213213

214214
@property
215-
def module(self):
215+
def module(self) -> str:
216216
match = self.pattern.match(self.value)
217+
assert match is not None
217218
return match.group('module')
218219

219220
@property
220-
def attr(self):
221+
def attr(self) -> str:
221222
match = self.pattern.match(self.value)
223+
assert match is not None
222224
return match.group('attr')
223225

224226
@property
225-
def extras(self):
227+
def extras(self) -> List[str]:
226228
match = self.pattern.match(self.value)
229+
assert match is not None
227230
return re.findall(r'\w+', match.group('extras') or '')
228231

229232
def _for(self, dist):
@@ -271,7 +274,7 @@ def __repr__(self):
271274
f'group={self.group!r})'
272275
)
273276

274-
def __hash__(self):
277+
def __hash__(self) -> int:
275278
return hash(self._key())
276279

277280

@@ -282,7 +285,7 @@ class EntryPoints(tuple):
282285

283286
__slots__ = ()
284287

285-
def __getitem__(self, name): # -> EntryPoint:
288+
def __getitem__(self, name: str) -> EntryPoint: # type: ignore[override]
286289
"""
287290
Get the EntryPoint in self matching name.
288291
"""
@@ -299,14 +302,14 @@ def select(self, **params):
299302
return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params))
300303

301304
@property
302-
def names(self):
305+
def names(self) -> Set[str]:
303306
"""
304307
Return the set of all names of all entry points.
305308
"""
306309
return {ep.name for ep in self}
307310

308311
@property
309-
def groups(self):
312+
def groups(self) -> Set[str]:
310313
"""
311314
Return the set of all groups of all entry points.
312315
"""
@@ -327,24 +330,28 @@ def _from_text(text):
327330
class PackagePath(pathlib.PurePosixPath):
328331
"""A reference to a path in a package"""
329332

330-
def read_text(self, encoding='utf-8'):
333+
hash: Optional["FileHash"]
334+
size: int
335+
dist: "Distribution"
336+
337+
def read_text(self, encoding: str = 'utf-8') -> str: # type: ignore[override]
331338
with self.locate().open(encoding=encoding) as stream:
332339
return stream.read()
333340

334-
def read_binary(self):
341+
def read_binary(self) -> bytes:
335342
with self.locate().open('rb') as stream:
336343
return stream.read()
337344

338-
def locate(self):
345+
def locate(self) -> pathlib.Path:
339346
"""Return a path-like object for this path"""
340347
return self.dist.locate_file(self)
341348

342349

343350
class FileHash:
344-
def __init__(self, spec):
351+
def __init__(self, spec: str) -> None:
345352
self.mode, _, self.value = spec.partition('=')
346353

347-
def __repr__(self):
354+
def __repr__(self) -> str:
348355
return f'<FileHash mode: {self.mode} value: {self.value}>'
349356

350357

@@ -379,14 +386,14 @@ def read_text(self, filename) -> Optional[str]:
379386
"""
380387

381388
@abc.abstractmethod
382-
def locate_file(self, path):
389+
def locate_file(self, path: StrPath) -> pathlib.Path:
383390
"""
384391
Given a path to a file in this distribution, return a path
385392
to it.
386393
"""
387394

388395
@classmethod
389-
def from_name(cls, name: str):
396+
def from_name(cls, name: str) -> "Distribution":
390397
"""Return the Distribution for the given package name.
391398
392399
:param name: The name of the distribution package to search for.
@@ -399,12 +406,12 @@ def from_name(cls, name: str):
399406
if not name:
400407
raise ValueError("A distribution name is required.")
401408
try:
402-
return next(cls.discover(name=name))
409+
return next(iter(cls.discover(name=name)))
403410
except StopIteration:
404411
raise PackageNotFoundError(name)
405412

406413
@classmethod
407-
def discover(cls, **kwargs):
414+
def discover(cls, **kwargs) -> Iterable["Distribution"]:
408415
"""Return an iterable of Distribution objects for all packages.
409416
410417
Pass a ``context`` or pass keyword arguments for constructing
@@ -422,7 +429,7 @@ def discover(cls, **kwargs):
422429
)
423430

424431
@staticmethod
425-
def at(path):
432+
def at(path: StrPath) -> "Distribution":
426433
"""Return a Distribution for the indicated metadata path
427434
428435
:param path: a string or path-like object
@@ -457,7 +464,7 @@ def metadata(self) -> _meta.PackageMetadata:
457464
return _adapters.Message(email.message_from_string(text))
458465

459466
@property
460-
def name(self):
467+
def name(self) -> str:
461468
"""Return the 'Name' metadata for the distribution package."""
462469
return self.metadata['Name']
463470

@@ -467,16 +474,16 @@ def _normalized_name(self):
467474
return Prepared.normalize(self.name)
468475

469476
@property
470-
def version(self):
477+
def version(self) -> str:
471478
"""Return the 'Version' metadata for the distribution package."""
472479
return self.metadata['Version']
473480

474481
@property
475-
def entry_points(self):
482+
def entry_points(self) -> EntryPoints:
476483
return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
477484

478485
@property
479-
def files(self):
486+
def files(self) -> Optional[List[PackagePath]]:
480487
"""Files in this distribution.
481488
482489
:return: List of PackagePath for this distribution or None
@@ -561,7 +568,7 @@ def _read_files_egginfo_sources(self):
561568
return text and map('"{}"'.format, text.splitlines())
562569

563570
@property
564-
def requires(self):
571+
def requires(self) -> Optional[List[str]]:
565572
"""Generated requirements specified for this Distribution"""
566573
reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
567574
return reqs and list(reqs)
@@ -640,7 +647,7 @@ def __init__(self, **kwargs):
640647
vars(self).update(kwargs)
641648

642649
@property
643-
def path(self):
650+
def path(self) -> List[str]:
644651
"""
645652
The sequence of directory path that a distribution finder
646653
should search.
@@ -651,7 +658,7 @@ def path(self):
651658
return vars(self).get('path', sys.path)
652659

653660
@abc.abstractmethod
654-
def find_distributions(self, context=Context()):
661+
def find_distributions(self, context=Context()) -> Iterable[Distribution]:
655662
"""
656663
Find distributions.
657664
@@ -786,7 +793,9 @@ class MetadataPathFinder(NullFinder, DistributionFinder):
786793
of Python that do not have a PathFinder find_distributions().
787794
"""
788795

789-
def find_distributions(self, context=DistributionFinder.Context()):
796+
def find_distributions(
797+
self, context=DistributionFinder.Context()
798+
) -> Iterable["PathDistribution"]:
790799
"""
791800
Find distributions.
792801
@@ -806,19 +815,19 @@ def _search_paths(cls, name, paths):
806815
path.search(prepared) for path in map(FastPath, paths)
807816
)
808817

809-
def invalidate_caches(cls):
818+
def invalidate_caches(cls) -> None:
810819
FastPath.__new__.cache_clear()
811820

812821

813822
class PathDistribution(Distribution):
814-
def __init__(self, path: SimplePath):
823+
def __init__(self, path: SimplePath) -> None:
815824
"""Construct a distribution.
816825
817826
:param path: SimplePath indicating the metadata directory.
818827
"""
819828
self._path = path
820829

821-
def read_text(self, filename):
830+
def read_text(self, filename: StrPath) -> Optional[str]:
822831
with suppress(
823832
FileNotFoundError,
824833
IsADirectoryError,
@@ -828,9 +837,11 @@ def read_text(self, filename):
828837
):
829838
return self._path.joinpath(filename).read_text(encoding='utf-8')
830839

840+
return None
841+
831842
read_text.__doc__ = Distribution.read_text.__doc__
832843

833-
def locate_file(self, path):
844+
def locate_file(self, path: StrPath) -> pathlib.Path:
834845
return self._path.parent / path
835846

836847
@property
@@ -863,7 +874,7 @@ def _name_from_stem(stem):
863874
return name
864875

865876

866-
def distribution(distribution_name):
877+
def distribution(distribution_name) -> Distribution:
867878
"""Get the ``Distribution`` instance for the named package.
868879
869880
:param distribution_name: The name of the distribution package as a string.
@@ -872,7 +883,7 @@ def distribution(distribution_name):
872883
return Distribution.from_name(distribution_name)
873884

874885

875-
def distributions(**kwargs):
886+
def distributions(**kwargs) -> Iterable[Distribution]:
876887
"""Get all ``Distribution`` instances in the current environment.
877888
878889
:return: An iterable of ``Distribution`` instances.
@@ -889,7 +900,7 @@ def metadata(distribution_name) -> _meta.PackageMetadata:
889900
return Distribution.from_name(distribution_name).metadata
890901

891902

892-
def version(distribution_name):
903+
def version(distribution_name) -> str:
893904
"""Get the version string for the named package.
894905
895906
:param distribution_name: The name of the distribution package to query.
@@ -923,7 +934,7 @@ def entry_points(**params) -> EntryPoints:
923934
return EntryPoints(eps).select(**params)
924935

925936

926-
def files(distribution_name):
937+
def files(distribution_name) -> Optional[List[PackagePath]]:
927938
"""Return a list of files for the named package.
928939
929940
:param distribution_name: The name of the distribution package to query.
@@ -932,11 +943,11 @@ def files(distribution_name):
932943
return distribution(distribution_name).files
933944

934945

935-
def requires(distribution_name):
946+
def requires(distribution_name) -> Optional[List[str]]:
936947
"""
937948
Return a list of requirements for the named package.
938949
939-
:return: An iterator of requirements, suitable for
950+
:return: An iterable of requirements, suitable for
940951
packaging.requirement.Requirement.
941952
"""
942953
return distribution(distribution_name).requires

importlib_metadata/_compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import os
12
import sys
23
import platform
34

5+
from typing import Union
6+
47

58
__all__ = ['install', 'NullFinder', 'Protocol']
69

@@ -70,3 +73,10 @@ def pypy_partial(val):
7073
"""
7174
is_pypy = platform.python_implementation() == 'PyPy'
7275
return val + is_pypy
76+
77+
78+
if sys.version_info >= (3, 9):
79+
StrPath = Union[str, os.PathLike[str]]
80+
else:
81+
# PathLike is only subscriptable at runtime in 3.9+
82+
StrPath = Union[str, "os.PathLike[str]"] # pragma: no cover

importlib_metadata/_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class SimplePath(Protocol[_T]):
4949
A minimal subset of pathlib.Path required by PathDistribution.
5050
"""
5151

52-
def joinpath(self) -> _T:
52+
def joinpath(self, other: Union[str, _T]) -> _T:
5353
... # pragma: no cover
5454

5555
def __truediv__(self, other: Union[str, _T]) -> _T:

0 commit comments

Comments
 (0)