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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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/38] 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 ======