From aa4fa92cfb9cd32e8d4a2cfe3f52e783209c32e7 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Tue, 28 Jun 2022 11:12:51 +0200 Subject: [PATCH 1/5] setuptools: add test for distutils' entrypoints using extras --- setuptools/tests/test_easy_install.py | 69 +++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 246d634f21..a4e5b2c29a 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -890,6 +890,75 @@ def test_setup_requires_with_transitive_extra_dependency( monkeypatch.setenv(str('PIP_TIMEOUT'), str('0')) run_setup(test_setup_py, [str('--version')]) + def test_setup_requires_with_distutils_command_dep(self, monkeypatch): + ''' + Use case: ensure build requirements' extras + are properly installed and activated. + ''' + with contexts.save_pkg_resources_state(): + with contexts.tempdir() as temp_dir: + # Create source distribution for `extra_dep`. + make_sdist(os.path.join(temp_dir, 'extra_dep-1.0.tar.gz'), [ + ('setup.py', + DALS(""" + import setuptools + setuptools.setup( + name='extra_dep', + version='1.0', + py_modules=['extra_dep'], + ) + """)), + ('setup.cfg', ''), + ('extra_dep.py', ''), + ]) + # Create source tree for `epdep`. + dep_pkg = os.path.join(temp_dir, 'epdep') + os.mkdir(dep_pkg) + path.build({ + 'setup.py': + DALS(""" + import setuptools + setuptools.setup( + name='dep', version='2.0', + py_modules=['epcmd'], + extras_require={'extra': ['extra_dep']}, + entry_points=''' + [distutils.commands] + epcmd = epcmd:epcmd [extra] + ''', + ) + """), + 'setup.cfg': '', + 'epcmd.py': DALS(""" + from distutils.command.build_py import build_py + + import extra_dep + + class epcmd(build_py): + pass + """), + }, prefix=dep_pkg) + # "Install" dep. + run_setup( + os.path.join(dep_pkg, 'setup.py'), [str('dist_info')]) + working_set.add_entry(dep_pkg) + # Create source tree for test package. + test_pkg = os.path.join(temp_dir, 'test_pkg') + test_setup_py = os.path.join(test_pkg, 'setup.py') + os.mkdir(test_pkg) + with open(test_setup_py, 'w') as fp: + fp.write(DALS( + ''' + from setuptools import installer, setup + setup(setup_requires='dep[extra]') + ''')) + # Check... + monkeypatch.setenv(str('PIP_FIND_LINKS'), str(temp_dir)) + monkeypatch.setenv(str('PIP_NO_INDEX'), str('1')) + monkeypatch.setenv(str('PIP_RETRIES'), str('0')) + monkeypatch.setenv(str('PIP_TIMEOUT'), str('0')) + run_setup(test_setup_py, ['epcmd']) + def make_trivial_sdist(dist_path, distname, version): """ From 36981f1419b4bcf88b074a8296f235f602e8e8c0 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Tue, 28 Jun 2022 11:16:58 +0200 Subject: [PATCH 2/5] setuptools: drop support for installing an entrypoint dependencies It does not work (broken since `v60.8.0`: the code in `_install_dependencies` forgets to add the newly installed egg to `sys.path`), and is unnecessary as it's the job of the code handling `setup_requires` to ensure all necessary build requirements are installed. --- setuptools/command/egg_info.py | 1 - setuptools/dist.py | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index c37ab81fa6..42a0178fce 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -296,7 +296,6 @@ def run(self): self.mkpath(self.egg_info) os.utime(self.egg_info, None) for ep in metadata.entry_points(group='egg_info.writers'): - self.distribution._install_dependencies(ep) writer = ep.load() writer(self, ep.name, os.path.join(self.egg_info, ep.name)) diff --git a/setuptools/dist.py b/setuptools/dist.py index c1ad30080b..8242354886 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -30,7 +30,6 @@ from setuptools.extern import packaging from setuptools.extern import ordered_set from setuptools.extern.more_itertools import unique_everseen, partition -from setuptools.extern import nspektr from ._importlib import metadata @@ -918,18 +917,8 @@ def _finalize_setup_keywords(self): for ep in metadata.entry_points(group='distutils.setup_keywords'): value = getattr(self, ep.name, None) if value is not None: - self._install_dependencies(ep) ep.load()(self, ep.name, value) - def _install_dependencies(self, ep): - """ - Given an entry point, ensure that any declared extras for - its distribution are installed. - """ - for req in nspektr.missing(ep): - # fetch_build_egg expects pkg_resources.Requirement - self.fetch_build_egg(pkg_resources.Requirement(str(req))) - def get_egg_cache_dir(self): egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): @@ -962,7 +951,6 @@ def get_command_class(self, command): eps = metadata.entry_points(group='distutils.commands', name=command) for ep in eps: - self._install_dependencies(ep) self.cmdclass[command] = cmdclass = ep.load() return cmdclass else: From 2b8a94c0d995583e1bec8a91ff8bb8c9ceec6ef7 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Tue, 28 Jun 2022 11:27:17 +0200 Subject: [PATCH 3/5] setuptools/vendored: drop `nspektr` No needed anymore. --- .../_vendor/nspektr-0.3.0.dist-info/INSTALLER | 1 - .../_vendor/nspektr-0.3.0.dist-info/LICENSE | 19 --- .../_vendor/nspektr-0.3.0.dist-info/METADATA | 57 ------- .../_vendor/nspektr-0.3.0.dist-info/RECORD | 11 -- .../_vendor/nspektr-0.3.0.dist-info/REQUESTED | 0 .../_vendor/nspektr-0.3.0.dist-info/WHEEL | 5 - .../nspektr-0.3.0.dist-info/top_level.txt | 1 - setuptools/_vendor/nspektr/__init__.py | 145 ------------------ setuptools/_vendor/nspektr/_compat.py | 21 --- setuptools/_vendor/vendored.txt | 1 - setuptools/extern/__init__.py | 2 +- tools/vendored.py | 11 -- 12 files changed, 1 insertion(+), 273 deletions(-) delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/REQUESTED delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL delete mode 100644 setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt delete mode 100644 setuptools/_vendor/nspektr/__init__.py delete mode 100644 setuptools/_vendor/nspektr/_compat.py diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER b/setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e38a..0000000000 --- a/setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE b/setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE deleted file mode 100644 index 353924be0e..0000000000 --- a/setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright Jason R. Coombs - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA b/setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA deleted file mode 100644 index aadc374911..0000000000 --- a/setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA +++ /dev/null @@ -1,57 +0,0 @@ -Metadata-Version: 2.1 -Name: nspektr -Version: 0.3.0 -Summary: package inspector -Home-page: https://github.com/jaraco/nspektr -Author: Jason R. Coombs -Author-email: jaraco@jaraco.com -License: UNKNOWN -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Requires-Python: >=3.7 -License-File: LICENSE -Requires-Dist: jaraco.context -Requires-Dist: jaraco.functools -Requires-Dist: more-itertools -Requires-Dist: packaging -Requires-Dist: importlib-metadata (>=3.6) ; python_version < "3.10" -Provides-Extra: docs -Requires-Dist: sphinx ; extra == 'docs' -Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs' -Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' -Provides-Extra: testing -Requires-Dist: pytest (>=6) ; extra == 'testing' -Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' -Requires-Dist: pytest-flake8 ; extra == 'testing' -Requires-Dist: pytest-cov ; extra == 'testing' -Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing' -Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' -Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing' - -.. image:: https://img.shields.io/pypi/v/nspektr.svg - :target: `PyPI link`_ - -.. image:: https://img.shields.io/pypi/pyversions/nspektr.svg - :target: `PyPI link`_ - -.. _PyPI link: https://pypi.org/project/nspektr - -.. image:: https://github.com/jaraco/nspektr/workflows/tests/badge.svg - :target: https://github.com/jaraco/nspektr/actions?query=workflow%3A%22tests%22 - :alt: tests - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black - :alt: Code style: Black - -.. .. 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 - :target: https://blog.jaraco.com/skeleton - - diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD b/setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD deleted file mode 100644 index 5e5de5eba6..0000000000 --- a/setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD +++ /dev/null @@ -1,11 +0,0 @@ -nspektr-0.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -nspektr-0.3.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 -nspektr-0.3.0.dist-info/METADATA,sha256=X0stV4vwFBDBxvzhBl4kAHVdGWPIjEitqAuTJItcQH0,2162 -nspektr-0.3.0.dist-info/RECORD,, -nspektr-0.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -nspektr-0.3.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 -nspektr-0.3.0.dist-info/top_level.txt,sha256=uEA20Ixo04XS3wOIt5-Jk5ZuMkBrtlleFipRr8Y1SjQ,8 -nspektr/__init__.py,sha256=d6-d-ZlGAQQP-MEi_NZMiyn2vLbq8Hw3HxICgm3X0Q8,3949 -nspektr/__pycache__/__init__.cpython-310.pyc,, -nspektr/__pycache__/_compat.cpython-310.pyc,, -nspektr/_compat.py,sha256=2QoozYhuhgow_NMUATmhoM-yppBV3jiZYQgdiP-ww0s,582 diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/REQUESTED b/setuptools/_vendor/nspektr-0.3.0.dist-info/REQUESTED deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL b/setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL deleted file mode 100644 index becc9a66ea..0000000000 --- a/setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.1) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt b/setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt deleted file mode 100644 index b10ef50ace..0000000000 --- a/setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -nspektr diff --git a/setuptools/_vendor/nspektr/__init__.py b/setuptools/_vendor/nspektr/__init__.py deleted file mode 100644 index 938bbdb980..0000000000 --- a/setuptools/_vendor/nspektr/__init__.py +++ /dev/null @@ -1,145 +0,0 @@ -import itertools -import functools -import contextlib - -from setuptools.extern.packaging.requirements import Requirement -from setuptools.extern.packaging.version import Version -from setuptools.extern.more_itertools import always_iterable -from setuptools.extern.jaraco.context import suppress -from setuptools.extern.jaraco.functools import apply - -from ._compat import metadata, repair_extras - - -def resolve(req: Requirement) -> metadata.Distribution: - """ - Resolve the requirement to its distribution. - - Ignore exception detail for Python 3.9 compatibility. - - >>> resolve(Requirement('pytest<3')) # doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - importlib.metadata.PackageNotFoundError: No package metadata was found for pytest<3 - """ - dist = metadata.distribution(req.name) - if not req.specifier.contains(Version(dist.version), prereleases=True): - raise metadata.PackageNotFoundError(str(req)) - dist.extras = req.extras # type: ignore - return dist - - -@apply(bool) -@suppress(metadata.PackageNotFoundError) -def is_satisfied(req: Requirement): - return resolve(req) - - -unsatisfied = functools.partial(itertools.filterfalse, is_satisfied) - - -class NullMarker: - @classmethod - def wrap(cls, req: Requirement): - return req.marker or cls() - - def evaluate(self, *args, **kwargs): - return True - - -def find_direct_dependencies(dist, extras=None): - """ - Find direct, declared dependencies for dist. - """ - simple = ( - req - for req in map(Requirement, always_iterable(dist.requires)) - if NullMarker.wrap(req).evaluate(dict(extra=None)) - ) - extra_deps = ( - req - for req in map(Requirement, always_iterable(dist.requires)) - for extra in always_iterable(getattr(dist, 'extras', extras)) - if NullMarker.wrap(req).evaluate(dict(extra=extra)) - ) - return itertools.chain(simple, extra_deps) - - -def traverse(items, visit): - """ - Given an iterable of items, traverse the items. - - For each item, visit is called to return any additional items - to include in the traversal. - """ - while True: - try: - item = next(items) - except StopIteration: - return - yield item - items = itertools.chain(items, visit(item)) - - -def find_req_dependencies(req): - with contextlib.suppress(metadata.PackageNotFoundError): - dist = resolve(req) - yield from find_direct_dependencies(dist) - - -def find_dependencies(dist, extras=None): - """ - Find all reachable dependencies for dist. - - dist is an importlib.metadata.Distribution (or similar). - TODO: create a suitable protocol for type hint. - - >>> deps = find_dependencies(resolve(Requirement('nspektr'))) - >>> all(isinstance(dep, Requirement) for dep in deps) - True - >>> not any('pytest' in str(dep) for dep in deps) - True - >>> test_deps = find_dependencies(resolve(Requirement('nspektr[testing]'))) - >>> any('pytest' in str(dep) for dep in test_deps) - True - """ - - def visit(req, seen=set()): - if req in seen: - return () - seen.add(req) - return find_req_dependencies(req) - - return traverse(find_direct_dependencies(dist, extras), visit) - - -class Unresolved(Exception): - def __iter__(self): - return iter(self.args[0]) - - -def missing(ep): - """ - Generate the unresolved dependencies (if any) of ep. - """ - return unsatisfied(find_dependencies(ep.dist, repair_extras(ep.extras))) - - -def check(ep): - """ - >>> ep, = metadata.entry_points(group='console_scripts', name='pip') - >>> check(ep) - >>> dist = metadata.distribution('nspektr') - - Since 'docs' extras are not installed, requesting them should fail. - - >>> ep = metadata.EntryPoint( - ... group=None, name=None, value='nspektr [docs]')._for(dist) - >>> check(ep) - Traceback (most recent call last): - ... - nspektr.Unresolved: [...] - """ - missed = list(missing(ep)) - if missed: - raise Unresolved(missed) diff --git a/setuptools/_vendor/nspektr/_compat.py b/setuptools/_vendor/nspektr/_compat.py deleted file mode 100644 index 3278379a04..0000000000 --- a/setuptools/_vendor/nspektr/_compat.py +++ /dev/null @@ -1,21 +0,0 @@ -import contextlib -import sys - - -if sys.version_info >= (3, 10): - import importlib.metadata as metadata -else: - import setuptools.extern.importlib_metadata as metadata # type: ignore # noqa: F401 - - -def repair_extras(extras): - """ - Repair extras that appear as match objects. - - python/importlib_metadata#369 revealed a flaw in the EntryPoint - implementation. This function wraps the extras to ensure - they are proper strings even on older implementations. - """ - with contextlib.suppress(AttributeError): - return list(item.group(0) for item in extras) - return extras diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt index 95de2dc52d..84c4006cd6 100644 --- a/setuptools/_vendor/vendored.txt +++ b/setuptools/_vendor/vendored.txt @@ -5,7 +5,6 @@ more_itertools==8.8.0 jaraco.text==3.7.0 importlib_resources==5.4.0 importlib_metadata==4.11.1 -nspektr==0.3.0 # required for importlib_metadata on older Pythons typing_extensions==4.0.1 # required for importlib_resources and _metadata on older Pythons diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py index 192e55f6e0..d3a6dc99fe 100644 --- a/setuptools/extern/__init__.py +++ b/setuptools/extern/__init__.py @@ -71,6 +71,6 @@ def install(self): names = ( 'packaging', 'pyparsing', 'ordered_set', 'more_itertools', 'importlib_metadata', - 'zipp', 'importlib_resources', 'jaraco', 'typing_extensions', 'nspektr', 'tomli', + 'zipp', 'importlib_resources', 'jaraco', 'typing_extensions', 'tomli', ) VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/tools/vendored.py b/tools/vendored.py index cd15adbf21..8a122ad778 100644 --- a/tools/vendored.py +++ b/tools/vendored.py @@ -89,16 +89,6 @@ def rewrite_more_itertools(pkg_files: Path): more_file.write_text(text) -def rewrite_nspektr(pkg_files: Path, new_root): - for file in pkg_files.glob('*.py'): - text = file.read_text() - text = re.sub(r' (more_itertools)', rf' {new_root}.\1', text) - text = re.sub(r' (jaraco\.\w+)', rf' {new_root}.\1', text) - text = re.sub(r' (packaging)', rf' {new_root}.\1', text) - text = re.sub(r' (importlib_metadata)', rf' {new_root}.\1', text) - file.write_text(text) - - def clean(vendor): """ Remove all files out of the vendor directory except the meta @@ -143,7 +133,6 @@ def update_setuptools(): rewrite_importlib_resources(vendor / 'importlib_resources', 'setuptools.extern') rewrite_importlib_metadata(vendor / 'importlib_metadata', 'setuptools.extern') rewrite_more_itertools(vendor / "more_itertools") - rewrite_nspektr(vendor / "nspektr", 'setuptools.extern') __name__ == '__main__' and update_vendored() From 26ff94e90a4da3e4faf085f9e9d02d1409585128 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sat, 2 Jul 2022 16:05:57 +0200 Subject: [PATCH 4/5] setuptools/tests: minor cleanup --- setuptools/tests/test_easy_install.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index a4e5b2c29a..d102e586b4 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -846,9 +846,11 @@ def test_setup_requires_with_find_links_in_setup_cfg( def test_setup_requires_with_transitive_extra_dependency( self, monkeypatch): - # Use case: installing a package with a build dependency on - # an already installed `dep[extra]`, which in turn depends - # on `extra_dep` (whose is not already installed). + ''' + Use case: installing a package with a build dependency on + an already installed `dep[extra]`, which in turn depends + on `extra_dep` (whose is not already installed). + ''' with contexts.save_pkg_resources_state(): with contexts.tempdir() as temp_dir: # Create source distribution for `extra_dep`. From dc64dfdb42d72f86b26ab592b1a71c91c9f2be70 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sat, 2 Jul 2022 16:18:24 +0200 Subject: [PATCH 5/5] add changelog entry --- changelog.d/3421.breaking.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog.d/3421.breaking.rst diff --git a/changelog.d/3421.breaking.rst b/changelog.d/3421.breaking.rst new file mode 100644 index 0000000000..003e6d33f7 --- /dev/null +++ b/changelog.d/3421.breaking.rst @@ -0,0 +1,4 @@ +Drop setuptools' support for installing an entrypoint extra requirements at load time: +- the functionality has been broken since v60.8.0. +- the mechanism to do so is deprecated (`fetch_build_eggs`). +- that use case (e.g. a custom command class entrypoint) is covered by making sure the necessary build requirements are declared.