From b2412262dc1dd5d3d697e551d86acee4d5519bb6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 30 Sep 2022 13:30:09 -0400 Subject: [PATCH 01/57] Indicate to use latest Python version (workaround for readthedocs/readthedocs.org/#9623). Requires also specifying the OS version (workaround for readthedocs/readthedocs.org#9635). --- .readthedocs.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index cc698548..6bef3493 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -4,3 +4,10 @@ python: - path: . extra_requirements: - docs + +# workaround for readthedocs/readthedocs.org#9623 +build: + # workaround for readthedocs/readthedocs.org#9635 + os: ubuntu-22.04 + tools: + python: "3" From 7e5bae4c7fbd30366e49249825171b193dff22d4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 18:38:15 -0400 Subject: [PATCH 02/57] Remove SelectableGroups --- importlib_metadata/__init__.py | 123 ++------------------------------- tests/test_api.py | 39 ----------- 2 files changed, 4 insertions(+), 158 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 0ad0196e..6abb375c 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -29,7 +29,7 @@ from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import List, Mapping, Optional, Union +from typing import List, Mapping, Optional __all__ = [ @@ -344,10 +344,6 @@ def names(self): def groups(self): """ Return the set of all groups of all entry points. - - For coverage while SelectableGroups is present. - >>> EntryPoints().groups - set() """ return set(ep.group for ep in self) @@ -367,109 +363,6 @@ def _parse_groups(text): ) -def flake8_bypass(func): - # defer inspect import as performance optimization. - import inspect - - is_flake8 = any('flake8' in str(frame.filename) for frame in inspect.stack()[:5]) - return func if not is_flake8 else lambda: None - - -class Deprecated: - """ - Compatibility add-in for mapping to indicate that - mapping behavior is deprecated. - - >>> recwarn = getfixture('recwarn') - >>> class DeprecatedDict(Deprecated, dict): pass - >>> dd = DeprecatedDict(foo='bar') - >>> dd.get('baz', None) - >>> dd['foo'] - 'bar' - >>> list(dd) - ['foo'] - >>> list(dd.keys()) - ['foo'] - >>> 'foo' in dd - True - >>> list(dd.values()) - ['bar'] - >>> len(recwarn) - 1 - """ - - _warn = functools.partial( - warnings.warn, - "SelectableGroups dict interface is deprecated. Use select.", - DeprecationWarning, - stacklevel=2, - ) - - def __getitem__(self, name): - self._warn() - return super().__getitem__(name) - - def get(self, name, default=None): - flake8_bypass(self._warn)() - return super().get(name, default) - - def __iter__(self): - self._warn() - return super().__iter__() - - def __contains__(self, *args): - self._warn() - return super().__contains__(*args) - - def keys(self): - self._warn() - return super().keys() - - def values(self): - self._warn() - return super().values() - - -class SelectableGroups(Deprecated, dict): - """ - A backward- and forward-compatible result from - entry_points that fully implements the dict interface. - """ - - @classmethod - def load(cls, eps): - by_group = operator.attrgetter('group') - ordered = sorted(eps, key=by_group) - grouped = itertools.groupby(ordered, by_group) - return cls((group, EntryPoints(eps)) for group, eps in grouped) - - @property - def _all(self): - """ - Reconstruct a list of all entrypoints from the groups. - """ - groups = super(Deprecated, self).values() - return EntryPoints(itertools.chain.from_iterable(groups)) - - @property - def groups(self): - return self._all.groups - - @property - def names(self): - """ - for coverage: - >>> SelectableGroups().names - set() - """ - return self._all.names - - def select(self, **params): - if not params: - return self - return self._all.select(**params) - - class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" @@ -964,29 +857,21 @@ def version(distribution_name): return distribution(distribution_name).version -def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: +def entry_points(**params) -> EntryPoints: """Return EntryPoint objects for all installed packages. Pass selection parameters (group or name) to filter the result to entry points matching those properties (see EntryPoints.select()). - For compatibility, returns ``SelectableGroups`` object unless - selection parameters are supplied. In the future, this function - will return ``EntryPoints`` instead of ``SelectableGroups`` - even when no selection parameters are supplied. - - For maximum future compatibility, pass selection parameters - or invoke ``.select`` with parameters on the result. - - :return: EntryPoints or SelectableGroups for all installed packages. + :return: EntryPoints for all installed packages. """ norm_name = operator.attrgetter('_normalized_name') unique = functools.partial(unique_everseen, key=norm_name) eps = itertools.chain.from_iterable( dist.entry_points for dist in unique(distributions()) ) - return SelectableGroups.load(eps).select(**params) + return EntryPoints(eps).select(**params) def files(distribution_name): diff --git a/tests/test_api.py b/tests/test_api.py index 819d4841..58bf2912 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -133,45 +133,6 @@ def test_entry_points_dict_construction(self): assert expected.category is DeprecationWarning assert "Construction of dict of EntryPoints is deprecated" in str(expected) - def test_entry_points_by_index(self): - """ - Prior versions of Distribution.entry_points would return a - tuple that allowed access by index. - Capture this now deprecated use-case - See python/importlib_metadata#300 and bpo-44246. - """ - eps = distribution('distinfo-pkg').entry_points - with warnings.catch_warnings(record=True) as caught: - eps[0] - - # check warning - expected = next(iter(caught)) - assert expected.category is DeprecationWarning - assert "Accessing entry points by index is deprecated" in str(expected) - - def test_entry_points_groups_getitem(self): - """ - Prior versions of entry_points() returned a dict. Ensure - that callers using '.__getitem__()' are supported but warned to - migrate. - """ - with warnings.catch_warnings(record=True): - entry_points()['entries'] == entry_points(group='entries') - - with self.assertRaises(KeyError): - entry_points()['missing'] - - def test_entry_points_groups_get(self): - """ - Prior versions of entry_points() returned a dict. Ensure - that callers using '.get()' are supported but warned to - migrate. - """ - with warnings.catch_warnings(record=True): - entry_points().get('missing', 'default') == 'default' - entry_points().get('entries', 'default') == entry_points()['entries'] - entry_points().get('missing', ()) == () - def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' From 47544ce7303da9b2147c6603a674a1f82225248f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 18:41:48 -0400 Subject: [PATCH 03/57] Remove DeprecatedList --- importlib_metadata/__init__.py | 95 +--------------------------------- 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 6abb375c..9908ac72 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -209,100 +209,7 @@ def matches(self, **params): return all(map(operator.eq, params.values(), attrs)) -class DeprecatedList(list): - """ - Allow an otherwise immutable object to implement mutability - for compatibility. - - >>> recwarn = getfixture('recwarn') - >>> dl = DeprecatedList(range(3)) - >>> dl[0] = 1 - >>> dl.append(3) - >>> del dl[3] - >>> dl.reverse() - >>> dl.sort() - >>> dl.extend([4]) - >>> dl.pop(-1) - 4 - >>> dl.remove(1) - >>> dl += [5] - >>> dl + [6] - [1, 2, 5, 6] - >>> dl + (6,) - [1, 2, 5, 6] - >>> dl.insert(0, 0) - >>> dl - [0, 1, 2, 5] - >>> dl == [0, 1, 2, 5] - True - >>> dl == (0, 1, 2, 5) - True - >>> len(recwarn) - 1 - """ - - _warn = functools.partial( - warnings.warn, - "EntryPoints list interface is deprecated. Cast to list if needed.", - DeprecationWarning, - stacklevel=2, - ) - - def __setitem__(self, *args, **kwargs): - self._warn() - return super().__setitem__(*args, **kwargs) - - def __delitem__(self, *args, **kwargs): - self._warn() - return super().__delitem__(*args, **kwargs) - - def append(self, *args, **kwargs): - self._warn() - return super().append(*args, **kwargs) - - def reverse(self, *args, **kwargs): - self._warn() - return super().reverse(*args, **kwargs) - - def extend(self, *args, **kwargs): - self._warn() - return super().extend(*args, **kwargs) - - def pop(self, *args, **kwargs): - self._warn() - return super().pop(*args, **kwargs) - - def remove(self, *args, **kwargs): - self._warn() - return super().remove(*args, **kwargs) - - def __iadd__(self, *args, **kwargs): - self._warn() - return super().__iadd__(*args, **kwargs) - - def __add__(self, other): - if not isinstance(other, tuple): - self._warn() - other = tuple(other) - return self.__class__(tuple(self) + other) - - def insert(self, *args, **kwargs): - self._warn() - return super().insert(*args, **kwargs) - - def sort(self, *args, **kwargs): - self._warn() - return super().sort(*args, **kwargs) - - def __eq__(self, other): - if not isinstance(other, tuple): - self._warn() - other = tuple(other) - - return tuple(self).__eq__(other) - - -class EntryPoints(DeprecatedList): +class EntryPoints(tuple): """ An immutable collection of selectable EntryPoint objects. """ From 0c819641d314ac496eb32b55f2b15215fa6fa55f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 18:43:09 -0400 Subject: [PATCH 04/57] Remove compatibility for EntryPoints.__getitem__ by index. --- importlib_metadata/__init__.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9908ac72..c243ff61 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -220,14 +220,6 @@ def __getitem__(self, name): # -> EntryPoint: """ Get the EntryPoint in self matching name. """ - if isinstance(name, int): - warnings.warn( - "Accessing entry points by index is deprecated. " - "Cast to tuple if needed.", - DeprecationWarning, - stacklevel=2, - ) - return super().__getitem__(name) try: return next(iter(self.select(name=name))) except StopIteration: From dde2b9de2973ce1c6fa9ba21dfe81069b0baa77b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 19:04:42 -0400 Subject: [PATCH 05/57] Remove support for cast of iterable of entry points to dict. Ref #97. --- importlib_metadata/__init__.py | 12 ------------ tests/test_api.py | 18 ------------------ tests/test_main.py | 10 ---------- 3 files changed, 40 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index c243ff61..86599a7a 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -8,7 +8,6 @@ import pathlib import operator import textwrap -import warnings import functools import itertools import posixpath @@ -187,17 +186,6 @@ def _for(self, dist): self.dist = dist return self - def __iter__(self): - """ - Supply iter so one may construct dicts of EntryPoints by name. - """ - msg = ( - "Construction of dict of EntryPoints is deprecated in " - "favor of EntryPoints." - ) - warnings.warn(msg, DeprecationWarning) - return iter((self.name, self)) - def __reduce__(self): return ( self.__class__, diff --git a/tests/test_api.py b/tests/test_api.py index 58bf2912..850e6ba6 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,7 +1,6 @@ import re import textwrap import unittest -import warnings import importlib from . import fixtures @@ -116,23 +115,6 @@ def test_entry_points_missing_name(self): def test_entry_points_missing_group(self): assert entry_points(group='missing') == () - def test_entry_points_dict_construction(self): - """ - Prior versions of entry_points() returned simple lists and - allowed casting those lists into maps by name using ``dict()``. - Capture this now deprecated use-case. - """ - with warnings.catch_warnings(record=True) as caught: - eps = dict(entry_points(group='entries')) - - assert 'main' in eps - assert eps['main'] == entry_points(group='entries')['main'] - - # check warning - expected = next(iter(caught)) - assert expected.category is DeprecationWarning - assert "Construction of dict of EntryPoints is deprecated" in str(expected) - def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' diff --git a/tests/test_main.py b/tests/test_main.py index f7c9c518..d315b781 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,9 +1,7 @@ import re -import json import pickle import textwrap import unittest -import warnings import importlib import importlib_metadata import pyfakefs.fake_filesystem_unittest as ffs @@ -243,14 +241,6 @@ def test_hashable(self): """EntryPoints should be hashable""" hash(self.ep) - def test_json_dump(self): - """ - json should not expect to be able to dump an EntryPoint - """ - with self.assertRaises(Exception): - with warnings.catch_warnings(record=True): - json.dumps(self.ep) - def test_module(self): assert self.ep.module == 'value' From 2135490a9d965339de71aa18dd2955844db966aa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 19:34:16 -0400 Subject: [PATCH 06/57] Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f48b9d20..5aa5776f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v5.0.0 +====== + +* #97, #284, #300: Removed compatibility shims for deprecated entry + point interfaces. + v4.13.0 ======= From 117d1b470f8c03b4f4e332a95ccfcfebdadb8f52 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 19:48:36 -0400 Subject: [PATCH 07/57] Disable flake8 due to incompatibility. --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 60de2424..27b0f18c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,8 @@ addopts = "--black" addopts = "--mypy" [tool.pytest-enabler.flake8] -addopts = "--flake8" +# disabled due to PyCQA/flake8#1438 +# addopts = "--flake8" [tool.pytest-enabler.cov] addopts = "--cov" From ac9ff953a00d86c341f2a9a892c914d26075d891 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Oct 2022 20:01:33 -0400 Subject: [PATCH 08/57] Update documentation around removal of SelectableGroups. --- docs/using.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 40616d86..bbb3824f 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -164,11 +164,10 @@ for more information on entry points, their definition, and usage. The "selectable" entry points were introduced in ``importlib_metadata`` 3.6 and Python 3.10. Prior to those changes, ``entry_points`` accepted no parameters and always returned a dictionary of entry points, keyed -by group. For compatibility, if no parameters are passed to entry_points, -a ``SelectableGroups`` object is returned, implementing that dict -interface. In the future, calling ``entry_points`` with no parameters -will return an ``EntryPoints`` object. Users should rely on the selection -interface to retrieve entry points by group. +by group. With ``importlib_metadata`` 5.0 and Python 3.12, +``entry_points`` always returns an ``EntryPoints`` object. See +`backports.entry_points_selectable `_ +for compatibility options. .. _metadata: From ecb363c061d4b24f8fb84621ebb5529dd8685f45 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 3 Oct 2022 09:30:48 -0400 Subject: [PATCH 09/57] Simply wrap .matches instead of replacing EntryPoint. --- importlib_metadata/__init__.py | 3 +-- importlib_metadata/_py39compat.py | 21 ++++----------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index eab5d4c1..33c632a2 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -382,8 +382,7 @@ def select(self, **params): Select entry points from self that match the given parameters (typically group and/or name). """ - candidates = (_py39compat.ep_matches(ep, **params) for ep in self) - return EntryPoints(ep for ep, predicate in candidates if predicate) + return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params)) @property def names(self): diff --git a/importlib_metadata/_py39compat.py b/importlib_metadata/_py39compat.py index cf9cc124..cde4558f 100644 --- a/importlib_metadata/_py39compat.py +++ b/importlib_metadata/_py39compat.py @@ -1,7 +1,7 @@ """ Compatibility layer with Python 3.8/3.9 """ -from typing import TYPE_CHECKING, Any, Optional, Tuple +from typing import TYPE_CHECKING, Any, Optional if TYPE_CHECKING: # pragma: no cover # Prevent circular imports on runtime. @@ -22,27 +22,14 @@ def normalized_name(dist: Distribution) -> Optional[str]: return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name']) -def ep_matches(ep: EntryPoint, **params) -> Tuple[EntryPoint, bool]: +def ep_matches(ep: EntryPoint, **params) -> bool: """ Workaround for ``EntryPoint`` objects without the ``matches`` method. - For the sake of convenience, a tuple is returned containing not only the - boolean value corresponding to the predicate evalutation, but also a compatible - ``EntryPoint`` object that can be safely used at a later stage. - - For example, the following sequences of expressions should be compatible: - - # Sequence 1: using the compatibility layer - candidates = (_py39compat.ep_matches(ep, **params) for ep in entry_points) - [ep for ep, predicate in candidates if predicate] - - # Sequence 2: using Python 3.9+ - [ep for ep in entry_points if ep.matches(**params)] """ try: - return ep, ep.matches(**params) + return ep.matches(**params) except AttributeError: from . import EntryPoint # -> delay to prevent circular imports. # Reconstruct the EntryPoint object to make sure it is compatible. - _ep = EntryPoint(ep.name, ep.value, ep.group) - return _ep, _ep.matches(**params) + return EntryPoint(ep.name, ep.value, ep.group).matches(**params) From 13438768ba4f2fea8e4c9407bc66674bafd598f0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 4 Oct 2022 11:50:53 -0400 Subject: [PATCH 10/57] Add minimum retention of DeprecatedTuple. Ref #409, Ref #348. --- importlib_metadata/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index f5967e41..26a1388c 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -139,6 +139,7 @@ class DeprecatedTuple: 1 """ + # Do not remove prior to 2023-05-01 or Python 3.13 _warn = functools.partial( warnings.warn, "EntryPoint tuple interface is deprecated. Access members by name.", From 9674092a543bd3e046eece53a26e0cb59e814fde Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 5 Oct 2022 15:08:07 -0400 Subject: [PATCH 11/57] Fix warning in plural of Import Package. --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index bbb3824f..7707078e 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -7,7 +7,7 @@ ``importlib_metadata`` is a library that provides access to the metadata of an installed :term:`packaging:Distribution Package`, such as its entry points -or its top-level names (:term:`packaging:Import Package`s, modules, if any). +or its top-level names (:term:`packaging:Import Package`\ s, modules, if any). Built in part on Python's import system, this library intends to replace similar functionality in the `entry point API`_ and `metadata API`_ of ``pkg_resources``. Along with From f75acec52c2eb8ded78ee753a95fd214b9f877a2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 6 Oct 2022 13:46:44 -0400 Subject: [PATCH 12/57] Correct syntax is without the space. --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 7707078e..37428fd0 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -7,7 +7,7 @@ ``importlib_metadata`` is a library that provides access to the metadata of an installed :term:`packaging:Distribution Package`, such as its entry points -or its top-level names (:term:`packaging:Import Package`\ s, modules, if any). +or its top-level names (:term:`packaging:Import Package`\s, modules, if any). Built in part on Python's import system, this library intends to replace similar functionality in the `entry point API`_ and `metadata API`_ of ``pkg_resources``. Along with From e95c54fe607aaa980a064b6490312483381ba0ab Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Oct 2022 11:35:13 -0400 Subject: [PATCH 13/57] GHA pretty env (#67) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🎨 Make the GHA log is clean and colorized This patch sets up root-level environment variables shared by all the workflow jobs. They include: * Disabling undesired `pip`'s warnings/suggestions * Requesting the executed apps color their output unconditionally * Letting `tox` pass those requests to underlying/wrapped programs * Reformat without end of line comments. Group into sections. * Avoid numerics for booleans where possible. Choose arbitrary numeric where any numeric is accepted. Co-authored-by: Sviatoslav Sydorenko --- .github/workflows/main.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 46e1ec9c..102e0e2b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,6 +2,36 @@ name: tests on: [push, pull_request] +env: + # Environment variables to support color support (jaraco/skeleton#66): + # Request colored output from CLI tools supporting it. Different tools + # interpret the value differently. For some, just being set is sufficient. + # For others, it must be a non-zero integer. For yet others, being set + # to a non-empty value is sufficient. + FORCE_COLOR: -106 + # MyPy's color enforcement (must be a non-zero number) + MYPY_FORCE_COLOR: -42 + # Recognized by the `py` package, dependency of `pytest` (must be "1") + PY_COLORS: 1 + # Make tox-wrapped tools see color requests + TOX_TESTENV_PASSENV: >- + FORCE_COLOR + MYPY_FORCE_COLOR + NO_COLOR + PY_COLORS + PYTEST_THEME + PYTEST_THEME_MODE + + # Suppress noisy pip warnings + PIP_DISABLE_PIP_VERSION_CHECK: 'true' + PIP_NO_PYTHON_VERSION_WARNING: 'true' + PIP_NO_WARN_SCRIPT_LOCATION: 'true' + + # Disable the spinner, noise in GHA; TODO(webknjaz): Fix this upstream + # Must be "1". + TOX_PARALLEL_NO_SPINNER: 1 + + jobs: test: strategy: From 54675240d4b4d2452a3777c5156f688e42a6c985 Mon Sep 17 00:00:00 2001 From: Zach Burnett Date: Thu, 13 Oct 2022 15:00:05 -0400 Subject: [PATCH 14/57] rename `.readthedocs.yml` to `.readthedocs.yaml` (RTD docs indicate that `.readthedocs.yml` will be deprecated) (#68) --- .readthedocs.yml => .readthedocs.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .readthedocs.yml => .readthedocs.yaml (100%) diff --git a/.readthedocs.yml b/.readthedocs.yaml similarity index 100% rename from .readthedocs.yml rename to .readthedocs.yaml From a0d28e5e792b20e81bc00bb07c9ff14b52d10924 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 17 Oct 2022 16:30:28 -0400 Subject: [PATCH 15/57] Add row for Python 3.12 to compatibility map. --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 47fa0cb2..08e06ea8 100644 --- a/README.rst +++ b/README.rst @@ -42,6 +42,8 @@ were contributed to different versions in the standard library: * - importlib_metadata - stdlib + * - 5.0 + - 3.12 * - 4.8 - 3.11 * - 4.4 From 88ad39d6b27f8bce591e1c8acb47094278534ce7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Oct 2022 12:36:44 -0400 Subject: [PATCH 16/57] Update compatibility matrix to reflect 4.13 in 3.11 (python/cpython#98875). --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 08e06ea8..c85645ee 100644 --- a/README.rst +++ b/README.rst @@ -44,7 +44,7 @@ were contributed to different versions in the standard library: - stdlib * - 5.0 - 3.12 - * - 4.8 + * - 4.13 - 3.11 * - 4.4 - 3.10 From 151032887aea82292b42b2f4b74263d79b62e167 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Tue, 18 Oct 2022 15:28:19 +0200 Subject: [PATCH 17/57] Doc: missing underscore in hyperlink. (GH-98391) --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 40616d86..03b30576 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -200,7 +200,7 @@ all the metadata in a JSON-compatible form per PEP 566:: The actual type of the object returned by ``metadata()`` is an implementation detail and should be accessed only through the interface described by the - `PackageMetadata protocol `. + `PackageMetadata protocol `_. .. _version: From 92498a6e6908368804565601de97d3432d1a0818 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 5 Nov 2022 11:49:03 -0400 Subject: [PATCH 18/57] Python 3.10 is synced mainly through 4.6 (plus bugfixes). --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c85645ee..e2cee859 100644 --- a/README.rst +++ b/README.rst @@ -46,7 +46,7 @@ were contributed to different versions in the standard library: - 3.12 * - 4.13 - 3.11 - * - 4.4 + * - 4.6 - 3.10 * - 1.4 - 3.8 From da84e5c7dabacf379165a0829b2f1741060ee2c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 8 Nov 2022 05:25:30 -0500 Subject: [PATCH 19/57] Pin mypy to '<0.990' due to realpython/pytest-mypy#141 --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index a0d86eba..503cbfda 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,8 @@ testing = pytest-mypy >= 0.9.1; \ # workaround for jaraco/skeleton#22 python_implementation != "PyPy" + # workaround for realpython/pytest-mypy#141 + mypy < 0.990 pytest-enabler >= 1.3 # local From f999a531587170b577da64d4bfb67a68b9aec106 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 21 Oct 2022 11:14:42 -0400 Subject: [PATCH 20/57] Remove the hyperlink for the Python versions badge. The PyPI badge is a better anchor for the hyperlink. --- README.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.rst b/README.rst index c82c6429..39459a4a 100644 --- a/README.rst +++ b/README.rst @@ -1,10 +1,7 @@ .. image:: https://img.shields.io/pypi/v/skeleton.svg - :target: `PyPI link`_ + :target: https://pypi.org/project/skeleton .. image:: https://img.shields.io/pypi/pyversions/skeleton.svg - :target: `PyPI link`_ - -.. _PyPI link: https://pypi.org/project/skeleton .. image:: https://github.com/jaraco/skeleton/workflows/tests/badge.svg :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22tests%22 From 401287d8d0f9fb0365149983f5ca42618f00a6d8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 9 Nov 2022 19:32:49 -0500 Subject: [PATCH 21/57] Apply explicit_package_bases for mypy and unpin the version. Ref python/mypy#14057. --- mypy.ini | 3 +++ setup.cfg | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy.ini b/mypy.ini index 976ba029..b6f97276 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,2 +1,5 @@ [mypy] ignore_missing_imports = True +# required to support namespace packages +# https://github.com/python/mypy/issues/14057 +explicit_package_bases = True diff --git a/setup.cfg b/setup.cfg index 503cbfda..a0d86eba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,8 +40,6 @@ testing = pytest-mypy >= 0.9.1; \ # workaround for jaraco/skeleton#22 python_implementation != "PyPy" - # workaround for realpython/pytest-mypy#141 - mypy < 0.990 pytest-enabler >= 1.3 # local From e16916fedcbeb41ba3e326b9b4fb0b66e660f3bd Mon Sep 17 00:00:00 2001 From: layday Date: Wed, 16 Nov 2022 22:09:56 +0200 Subject: [PATCH 22/57] Fix `SimplePath` protocol This makes `pathlib.Path`s and `zipfile.Path`s assignable to the protocol. --- importlib_metadata/_meta.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/importlib_metadata/_meta.py b/importlib_metadata/_meta.py index 37ee43e6..259b15ba 100644 --- a/importlib_metadata/_meta.py +++ b/importlib_metadata/_meta.py @@ -30,18 +30,19 @@ def json(self) -> Dict[str, Union[str, List[str]]]: """ -class SimplePath(Protocol): +class SimplePath(Protocol[_T]): """ A minimal subset of pathlib.Path required by PathDistribution. """ - def joinpath(self) -> 'SimplePath': + def joinpath(self) -> _T: ... # pragma: no cover - def __truediv__(self) -> 'SimplePath': + def __truediv__(self, other: Union[str, _T]) -> _T: ... # pragma: no cover - def parent(self) -> 'SimplePath': + @property + def parent(self) -> _T: ... # pragma: no cover def read_text(self) -> str: From 56b6f1d1d7a975b27f96c4e15a20077914b4c554 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 18 Nov 2022 22:32:51 -0500 Subject: [PATCH 23/57] Add Python 3.12 to matrix. Only test 3.8-3.10 on Linux. --- .github/workflows/main.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 102e0e2b..3a28be36 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,8 +38,8 @@ jobs: matrix: python: - "3.7" - - "3.10" - "3.11" + - "3.12" # Workaround for actions/setup-python#508 dev: - -dev @@ -48,6 +48,12 @@ jobs: - macos-latest - windows-latest include: + - python: "3.8" + platform: ubuntu-latest + - python: "3.9" + platform: ubuntu-latest + - python: "3.10" + platform: ubuntu-latest - python: pypy3.9 platform: ubuntu-latest runs-on: ${{ matrix.platform }} From 9e13598ce4b81c2c964dd555fa407bb3ba4cc607 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Nov 2022 09:36:01 -0500 Subject: [PATCH 24/57] Disable flake8 on Python 3.12. Workaround for tholo/pytest-flake8#87. --- setup.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index a0d86eba..a8f80ced 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,9 @@ testing = # upstream pytest >= 6 pytest-checkdocs >= 2.4 - pytest-flake8 + pytest-flake8; \ + # workaround for tholo/pytest-flake8#87 + python_version < "3.12" # workaround for tholo/pytest-flake8#87 flake8 < 5 pytest-black >= 0.3.7; \ From c7d639e7da133d8ed8027f4c40bf477b8a447459 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 24 Nov 2022 08:52:56 -0500 Subject: [PATCH 25/57] Update changelog. --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5aa5776f..cf7dcf16 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,8 @@ +v5.1.0 +====== + +* #415: Instrument ``SimplePath`` with generic support. + v5.0.0 ====== From b74765da2d794941012b145bf228eeabc42ba73b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 24 Nov 2022 09:02:28 -0500 Subject: [PATCH 26/57] Add note to docs about limitation of packages_distributions. Fixes #402. --- docs/using.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/using.rst b/docs/using.rst index f3f63176..831ad62b 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -289,6 +289,10 @@ Python module or :term:`packaging:Import Package`:: >>> packages_distributions() {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...} +Some editable installs, `do not supply top-level names +`_, and thus this +function is not reliable with such installs. + .. _distributions: Distributions From 9708c37ef0d286c4e907adc59f46cc92262e3bf1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 6 Dec 2022 09:10:17 -0500 Subject: [PATCH 27/57] Honor ResourceWarnings. Fixes jaraco/skeleton#73. --- pytest.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytest.ini b/pytest.ini index 80e98cc9..2c2817b8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,6 +3,9 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= + # Ensure ResourceWarnings are emitted + default::ResourceWarning + # Suppress deprecation warning in flake8 ignore:SelectableGroups dict interface is deprecated::flake8 From 86a55c8320e2706d0f92e3248c29351bff83da4b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 8 Dec 2022 19:18:02 -0500 Subject: [PATCH 28/57] tox 4 requires a boolean value, so use '1' to FORCE_COLOR. Fixes jaraco/skeleton#74. --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3a28be36..e1e7bf19 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,8 +7,10 @@ env: # Request colored output from CLI tools supporting it. Different tools # interpret the value differently. For some, just being set is sufficient. # For others, it must be a non-zero integer. For yet others, being set - # to a non-empty value is sufficient. - FORCE_COLOR: -106 + # to a non-empty value is sufficient. For tox, it must be one of + # , 0, 1, false, no, off, on, true, yes. The only enabling value + # in common is "1". + FORCE_COLOR: 1 # MyPy's color enforcement (must be a non-zero number) MYPY_FORCE_COLOR: -42 # Recognized by the `py` package, dependency of `pytest` (must be "1") From ef521390cb51a12eab5c4155900f45dc2c89d507 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 Dec 2022 23:17:14 -0500 Subject: [PATCH 29/57] Remove unnecessary shebang and encoding header in docs conf. --- docs/conf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fa741a85..c2043393 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - extensions = [ 'sphinx.ext.autodoc', 'jaraco.packaging.sphinx', From c68ac3b7a3001502f681722dc55dff70a3169276 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Dec 2022 21:04:34 -0500 Subject: [PATCH 30/57] Prevent Python 3.12 from blocking checks. --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e1e7bf19..9d02856b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,6 +59,7 @@ jobs: - python: pypy3.9 platform: ubuntu-latest runs-on: ${{ matrix.platform }} + continue-on-error: ${{ matrix.python == '3.12' }} steps: - uses: actions/checkout@v3 - name: Setup Python From 82465b907d5131a57862a7242d64d610c3a05039 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 17 Dec 2022 20:46:15 -0500 Subject: [PATCH 31/57] Build docs in CI, including sphinx-lint. --- .github/workflows/main.yml | 17 +++++++++++++++++ setup.cfg | 1 + tox.ini | 1 + 3 files changed, 19 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9d02856b..9629a26a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,11 +72,28 @@ jobs: - name: Run tests run: tox + docs: + runs-on: ubuntu-latest + env: + TOXENV: docs + steps: + - uses: actions/checkout@v3 + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }}${{ matrix.dev }} + - name: Install tox + run: | + python -m pip install tox + - name: Run tests + run: tox + check: # This job does nothing and is only used for the branch protection if: always() needs: - test + - docs runs-on: ubuntu-latest diff --git a/setup.cfg b/setup.cfg index a8f80ced..c062c7b9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,6 +52,7 @@ docs = jaraco.packaging >= 9 rst.linker >= 1.9 furo + sphinx-lint # local diff --git a/tox.ini b/tox.ini index 3ca2af38..42ae6852 100644 --- a/tox.ini +++ b/tox.ini @@ -20,6 +20,7 @@ extras = changedir = docs commands = python -m sphinx -W --keep-going . {toxinidir}/build/html + python -m sphinxlint [testenv:release] skip_install = True From 880a6219a16911817214827020f272b4b03b54b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Dec 2022 16:37:30 -0500 Subject: [PATCH 32/57] Mark `PackageMetadata.__getitem__` as deprecated for missing values. Ref #371. --- CHANGES.rst | 7 +++++++ importlib_metadata/_adapters.py | 22 ++++++++++++++++++++++ tests/test_api.py | 8 ++++++++ 3 files changed, 37 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cf7dcf16..a2df91a3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v5.2.0 +====== + +* #371: Deprecated expectation that ``PackageMetadata.__getitem__`` + will return ``None`` for missing keys. In the future, it will raise a + ``KeyError``. + v5.1.0 ====== diff --git a/importlib_metadata/_adapters.py b/importlib_metadata/_adapters.py index aa460d3e..e33cba5e 100644 --- a/importlib_metadata/_adapters.py +++ b/importlib_metadata/_adapters.py @@ -1,8 +1,20 @@ +import functools +import warnings import re import textwrap import email.message from ._text import FoldedCase +from ._compat import pypy_partial + + +# Do not remove prior to 2024-01-01 or Python 3.14 +_warn = functools.partial( + warnings.warn, + "Implicit None on return values is deprecated and will raise KeyErrors.", + DeprecationWarning, + stacklevel=pypy_partial(2), +) class Message(email.message.Message): @@ -39,6 +51,16 @@ def __init__(self, *args, **kwargs): def __iter__(self): return super().__iter__() + def __getitem__(self, item): + """ + Warn users that a ``KeyError`` can be expected when a + mising key is supplied. Ref python/importlib_metadata#371. + """ + res = super().__getitem__(item) + if res is None: + _warn() + return res + def _repair_headers(self): def redent(value): "Correct for RFC822 indentation" diff --git a/tests/test_api.py b/tests/test_api.py index f65287a5..504d0553 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -141,6 +141,14 @@ def test_importlib_metadata_version(self): resolved = version('importlib-metadata') assert re.match(self.version_pattern, resolved) + def test_missing_key_legacy(self): + """ + Requesting a missing key will still return None, but warn. + """ + md = metadata('distinfo-pkg') + with suppress_known_deprecation(): + assert md['does-not-exist'] is None + @staticmethod def _test_files(files): root = files[0].root From a6c6660d71fcd9f55d4ddbb4cd411ab34cc38ec9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2022 19:55:26 -0500 Subject: [PATCH 33/57] Put tidelift docs dependency in its own section to limit merge conflicts. --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 798b1033..cdb0caa9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,5 @@ [options.extras_require] docs = - # upstream + + # tidelift jaraco.tidelift >= 1.4 From cd68fe5f36f79da1c9710211958308afd1c8bc69 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2023 12:40:44 -0500 Subject: [PATCH 34/57] Remove test for 'new style classes', no longer relevant in a world where all classes are new style. --- tests/test_main.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 514bfadf..b5545e54 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -9,7 +9,6 @@ from importlib_metadata import ( Distribution, EntryPoint, - MetadataPathFinder, PackageNotFoundError, _unique, distributions, @@ -44,10 +43,6 @@ def test_package_not_found_mentions_metadata(self): assert "metadata" in str(ctx.exception) - def test_new_style_classes(self): - self.assertIsInstance(Distribution, type) - self.assertIsInstance(MetadataPathFinder, type) - @fixtures.parameterize( dict(name=None), dict(name=''), From 4a4f062a5122d637cf0358cf05642655ccbafba6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2023 12:41:23 -0500 Subject: [PATCH 35/57] Correct typo --- tests/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index b5545e54..7b8d797f 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -34,7 +34,7 @@ def test_for_name_does_not_exist(self): def test_package_not_found_mentions_metadata(self): """ When a package is not found, that could indicate that the - packgae is not installed or that it is installed without + package is not installed or that it is installed without metadata. Ensure the exception mentions metadata to help guide users toward the cause. See #124. """ From 1bf8a7aec366ede912b9f9c989006cc358cf2cdc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2023 12:10:08 -0500 Subject: [PATCH 36/57] Add xfail test capturing desired expectation. --- tests/test_main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_main.py b/tests/test_main.py index 7b8d797f..cec82121 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -43,6 +43,11 @@ def test_package_not_found_mentions_metadata(self): assert "metadata" in str(ctx.exception) + def test_abc_enforced(self): + with self.assertRaises(AssertionError): # xfail + with self.assertRaises(TypeError): + type('DistributionSubclass', (Distribution,), {})() + @fixtures.parameterize( dict(name=None), dict(name=''), From 8a9d1699aa47d71da6fb385b8510bf95fed6e3e2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2023 12:35:46 -0500 Subject: [PATCH 37/57] Add ABCMeta to Distribution. Fixes #419. --- importlib_metadata/__init__.py | 2 +- tests/test_main.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 26a1388c..9a36a8e6 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -346,7 +346,7 @@ def __repr__(self): return f'' -class Distribution: +class Distribution(metaclass=abc.ABCMeta): """A Python distribution package.""" @abc.abstractmethod diff --git a/tests/test_main.py b/tests/test_main.py index cec82121..f0f84983 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -44,9 +44,8 @@ def test_package_not_found_mentions_metadata(self): assert "metadata" in str(ctx.exception) def test_abc_enforced(self): - with self.assertRaises(AssertionError): # xfail - with self.assertRaises(TypeError): - type('DistributionSubclass', (Distribution,), {})() + with self.assertRaises(TypeError): + type('DistributionSubclass', (Distribution,), {})() @fixtures.parameterize( dict(name=None), From 2d52ecd7a581e97012830e7d18932408729d0e9a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2023 12:39:00 -0500 Subject: [PATCH 38/57] Update changelog. Ref #419. --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a2df91a3..4dd9d5df 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,15 @@ +v6.0.0 +====== + +* #419: Declared ``Distribution`` as an abstract class, enforcing + definition of abstract methods in instantiated subclasses. It's no + longer possible to instantiate a ``Distribution`` or any subclasses + unless they define the abstract methods. + + Please comment in the issue if this change breaks any projects. + This change will likely be rolled back if it causes significant + disruption. + v5.2.0 ====== From eb2bdc83a7d3cfd1c2bc3aeae39a900d654a6839 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 2 Jan 2023 03:17:24 -0500 Subject: [PATCH 39/57] Update badge for 2023 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 39459a4a..af0efb05 100644 --- a/README.rst +++ b/README.rst @@ -14,5 +14,5 @@ .. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest .. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2022-informational +.. image:: https://img.shields.io/badge/skeleton-2023-informational :target: https://blog.jaraco.com/skeleton From f9e01d2197d18b2b21976bae6e5b7f90b683bc4f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 18 Jan 2023 21:33:18 -0500 Subject: [PATCH 40/57] ALLOW_UNICODE no longer needed on Python 3. As a result, ELLIPSES is also now enabled by default. --- pytest.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 2c2817b8..1e6adf08 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs addopts=--doctest-modules -doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= # Ensure ResourceWarnings are emitted default::ResourceWarning From 284359e5123eb6a9f975092d1fb17dfa814d1594 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 27 Jan 2023 17:56:30 -0500 Subject: [PATCH 41/57] Enable default encoding warning where available. See PEP 597. --- tox.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 42ae6852..5a678211 100644 --- a/tox.ini +++ b/tox.ini @@ -8,10 +8,13 @@ toxworkdir={env:TOX_WORK_DIR:.tox} [testenv] deps = +setenv = + PYTHONWARNDEFAULTENCODING = 1 commands = pytest {posargs} usedevelop = True -extras = testing +extras = + testing [testenv:docs] extras = From f18255faba76a6a86bf3fa6f73da9d974262aebd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 27 Jan 2023 18:19:23 -0500 Subject: [PATCH 42/57] Suppress EncodingWarning in pytest_black. Workaround for shopkeep/pytest-black#67. --- pytest.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytest.ini b/pytest.ini index 1e6adf08..bd7d0b52 100644 --- a/pytest.ini +++ b/pytest.ini @@ -17,3 +17,6 @@ filterwarnings= ignore: is not using a cooperative constructor:pytest.PytestDeprecationWarning ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning ignore:Flake8Item is an Item subclass and should not be a collector:pytest.PytestWarning + + # shopkeep/pytest-black#67 + ignore:'encoding' argument not specified::pytest_black From 0d9c6f0f5b6182cdac448270dbc0529f91b50bd9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 27 Jan 2023 18:50:22 -0500 Subject: [PATCH 43/57] Exempt warning. Workaround for realpython/pytest-mypy#152 --- pytest.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytest.ini b/pytest.ini index bd7d0b52..69d95b26 100644 --- a/pytest.ini +++ b/pytest.ini @@ -20,3 +20,6 @@ filterwarnings= # shopkeep/pytest-black#67 ignore:'encoding' argument not specified::pytest_black + + # realpython/pytest-mypy#152 + ignore:'encoding' argument not specified::pytest_mypy From 5f095d18d76f7ae36e57fa3241da341b0f9cd365 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Feb 2023 09:54:15 -0500 Subject: [PATCH 44/57] Add #upstream markers for filtered warnings. Add filter for platform module (ref python/cpython#100750). --- pytest.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pytest.ini b/pytest.ini index 69d95b26..5b6ddc45 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,6 +2,8 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules filterwarnings= + ## upstream + # Ensure ResourceWarnings are emitted default::ResourceWarning @@ -23,3 +25,8 @@ filterwarnings= # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy + + # python/cpython#100750 + ignore::EncodingWarning:platform + + ## end upstream From 6f7ac885c61eb74df8c2db435cdbec412da06fe6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 Feb 2023 03:52:03 -0500 Subject: [PATCH 45/57] Remove reference to EncodingWarning as it doesn't exist on some Pythons. --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 5b6ddc45..99a25199 100644 --- a/pytest.ini +++ b/pytest.ini @@ -27,6 +27,6 @@ filterwarnings= ignore:'encoding' argument not specified::pytest_mypy # python/cpython#100750 - ignore::EncodingWarning:platform + ignore:'encoding' argument not specified::platform ## end upstream From 9650fc184fc120a21623d8f92d03ee4ccbaa89d8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 16 Feb 2023 16:43:22 -0500 Subject: [PATCH 46/57] Revert "exclude build env from cov reporting (jaraco/skeleton#60)" This reverts commit e719f86c138a750f0c4599cd01cb8067b1ca95c8. The issue seems to have been addressed somehow. Ref pytest-dev/pytest-cov#538. --- .coveragerc | 1 - 1 file changed, 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 01164f62..6a34e662 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,7 +2,6 @@ omit = # leading `*/` for pytest-dev/pytest-cov#456 */.tox/* - */pep517-build-env-* [report] show_missing = True From 56cdf46aa19450d58b4a56af6553a0225762ae4b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 16 Feb 2023 21:12:36 -0500 Subject: [PATCH 47/57] Disable couldnt-parse warnings. Prescribed workaround for nedbat/coveragepy#1392. Fixes python/importlib_resources#279 and fixes jaraco/skeleton#56. --- .coveragerc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.coveragerc b/.coveragerc index 6a34e662..02879483 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,8 @@ omit = # leading `*/` for pytest-dev/pytest-cov#456 */.tox/* +disable_warnings = + couldnt-parse [report] show_missing = True From 56aad0ffe7f2e72500cc45f7e4ba6bee014364bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 10 Mar 2023 02:56:16 +0000 Subject: [PATCH 48/57] Sync PackageMetadata with email.message.Message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- importlib_metadata/_meta.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/importlib_metadata/_meta.py b/importlib_metadata/_meta.py index 259b15ba..6123e746 100644 --- a/importlib_metadata/_meta.py +++ b/importlib_metadata/_meta.py @@ -1,5 +1,5 @@ from ._compat import Protocol -from typing import Any, Dict, Iterator, List, TypeVar, Union +from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload _T = TypeVar("_T") @@ -18,7 +18,12 @@ def __getitem__(self, key: str) -> str: def __iter__(self) -> Iterator[str]: ... # pragma: no cover - def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]: + @overload + def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]: + ... + + @overload + def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]: """ Return all values associated with a possibly multi-valued key. """ From 5475a6e3d96e1f31441c90386e8d70514ab0e954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 10 Mar 2023 02:36:44 +0000 Subject: [PATCH 49/57] Fix formatting in test_api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- tests/test_api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index 504d0553..2932c2d2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -32,7 +32,6 @@ class APITests( fixtures.EggInfoFile, unittest.TestCase, ): - version_pattern = r'\d+\.\d+(\.\d)?' def test_retrieves_version_of_self(self): From fb364de47af2f12b93f4ab6c156a268aceded81e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 11:49:06 -0400 Subject: [PATCH 50/57] Add comment to link rationale for existence. --- importlib_metadata/_meta.py | 1 + 1 file changed, 1 insertion(+) diff --git a/importlib_metadata/_meta.py b/importlib_metadata/_meta.py index 6123e746..d6ba1c30 100644 --- a/importlib_metadata/_meta.py +++ b/importlib_metadata/_meta.py @@ -18,6 +18,7 @@ def __getitem__(self, key: str) -> str: def __iter__(self) -> Iterator[str]: ... # pragma: no cover + # overload per python/importlib_metadata#435 @overload def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]: ... From 51b89b07d828cf0e2137743a61672a1b57f88c73 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 11:49:50 -0400 Subject: [PATCH 51/57] =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20?= =?UTF-8?q?(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_metadata/_meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importlib_metadata/_meta.py b/importlib_metadata/_meta.py index d6ba1c30..8621c680 100644 --- a/importlib_metadata/_meta.py +++ b/importlib_metadata/_meta.py @@ -21,7 +21,7 @@ def __iter__(self) -> Iterator[str]: # overload per python/importlib_metadata#435 @overload def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]: - ... + ... # pragma: no cover @overload def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]: From ac0949adceae61c7cf31c009635e02952cccc4b0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 11:51:35 -0400 Subject: [PATCH 52/57] Update changelog. --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4dd9d5df..8eca4dfb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v6.0.1 +====== + +* #434: Expand protocol for ``PackageMetadata.get_all`` to match + the upstream implementation of ``email.message.Message.get_all`` + in python/typeshed#9620. + v6.0.0 ====== From a046b04cd628eff4b21a0e6e77d3f9a2672e311b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 12:07:51 -0400 Subject: [PATCH 53/57] Extend the workaround to satisfy docstring of typing._overload_dummy. --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index ebfdb74d..8e7762d0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -61,9 +61,11 @@ ), ) -# Workaround for #316 nitpick_ignore = [ + # Workaround for #316 ('py:class', 'importlib_metadata.EntryPoints'), ('py:class', 'importlib_metadata.SelectableGroups'), ('py:class', 'importlib_metadata._meta._T'), + # Workaround for #435 + ('py:class', '_T'), ] From 56aee0332f3a5a637b9de97a68f5b173f4ee8549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 10 Mar 2023 00:57:43 +0000 Subject: [PATCH 54/57] Add missing modules to packages_distributions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- importlib_metadata/__init__.py | 14 +++++++++----- tests/test_main.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9a36a8e6..9428e7d0 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -13,6 +13,7 @@ import itertools import posixpath import collections +import inspect from . import _adapters, _meta, _py39compat from ._collections import FreezableDefaultDict, Pair @@ -897,8 +898,11 @@ def _top_level_declared(dist): def _top_level_inferred(dist): - return { - f.parts[0] if len(f.parts) > 1 else f.with_suffix('').name - for f in always_iterable(dist.files) - if f.suffix == ".py" - } + return filter( + None, + { + # this logic relies on the assumption that dist.files only contains files (not directories) + inspect.getmodulename(f) if len(f.parts) == 1 else f.parts[0] + for f in always_iterable(dist.files) + }, + ) diff --git a/tests/test_main.py b/tests/test_main.py index f0f84983..16367793 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -322,3 +322,34 @@ def test_packages_distributions_neither_toplevel_nor_files(self): prefix=self.site_dir, ) packages_distributions() + + def test_packages_distributions_all_module_types(self): + """ + Test top-level modules detected on a package without 'top-level.txt'. + """ + suffixes = importlib.machinery.all_suffixes() + fixtures.build_files( + { + 'all_distributions-1.0.0.dist-info': { + 'METADATA': """ + Name: all_distributions + Version: 1.0.0 + """, + 'RECORD': ''.join( + f'{i}-top-level{suffix},,\n' + f'{i}-in-namespace/mod{suffix},,\n' + f'{i}-in-package/__init__.py,,\n' + f'{i}-in-package/mod{suffix},,\n' + for i, suffix in enumerate(suffixes) + ), + }, + }, + prefix=self.site_dir, + ) + + distributions = packages_distributions() + + for i in range(len(suffixes)): + assert distributions[f'{i}-top-level'] == ['all_distributions'] + assert distributions[f'{i}-in-namespace'] == ['all_distributions'] + assert distributions[f'{i}-in-package'] == ['all_distributions'] From a3c066ddd3c0780c6e64364f1917b06b9795b330 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 12:49:27 -0400 Subject: [PATCH 55/57] Remove long-line comment. --- importlib_metadata/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9428e7d0..1d300ddb 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -901,7 +901,6 @@ def _top_level_inferred(dist): return filter( None, { - # this logic relies on the assumption that dist.files only contains files (not directories) inspect.getmodulename(f) if len(f.parts) == 1 else f.parts[0] for f in always_iterable(dist.files) }, From 6610368e5952f2780f347b9c8af060f9984e0846 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 12:52:20 -0400 Subject: [PATCH 56/57] Extract variable for optional names. --- importlib_metadata/__init__.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 1d300ddb..656a79dc 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -898,10 +898,8 @@ def _top_level_declared(dist): def _top_level_inferred(dist): - return filter( - None, - { - inspect.getmodulename(f) if len(f.parts) == 1 else f.parts[0] - for f in always_iterable(dist.files) - }, - ) + opt_names = { + inspect.getmodulename(f) if len(f.parts) == 1 else f.parts[0] + for f in always_iterable(dist.files) + } + return filter(None, opt_names) From df7824b2ca587b073b08d25e306a68b5d61a960c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Mar 2023 12:54:34 -0400 Subject: [PATCH 57/57] Restore logic for parts. --- CHANGES.rst | 7 +++++++ importlib_metadata/__init__.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8eca4dfb..eccdd5ba 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v6.1.0 +====== + +* #428: ``packages_distributions`` now honors packages and modules + with Python modules that not ``.py`` sources (e.g. ``.pyc``, + ``.so``). + v6.0.1 ====== diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 656a79dc..8d9f0016 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -899,7 +899,7 @@ def _top_level_declared(dist): def _top_level_inferred(dist): opt_names = { - inspect.getmodulename(f) if len(f.parts) == 1 else f.parts[0] + f.parts[0] if len(f.parts) > 1 else inspect.getmodulename(f) for f in always_iterable(dist.files) } return filter(None, opt_names)