summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2022-03-13 14:01:16 -0400
committerGitHub <noreply@github.com>2022-03-13 14:01:16 -0400
commitba25a5f0287177f19574c577db370c684b51b415 (patch)
tree330dbaaf858eb83af7534241fd415855a9cd2c56
parentfc72349c9397f2f03e971d8ebc3e4928957180f1 (diff)
parente7b99faf0add4e9bbc1970a975c371657a125e70 (diff)
downloadpython-setuptools-git-ba25a5f0287177f19574c577db370c684b51b415.tar.gz
Merge pull request #3170 from pypa/feature/nspektr
Vendor nspektr. Utilize it in Distribution._install_dependencies.
-rw-r--r--changelog.d/3170.change.rst1
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA57
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD11
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/nspektr/__init__.py145
-rw-r--r--setuptools/_vendor/nspektr/_compat.py21
-rw-r--r--setuptools/_vendor/vendored.txt1
-rw-r--r--setuptools/dist.py22
-rw-r--r--setuptools/extern/__init__.py2
-rw-r--r--tools/vendored.py11
14 files changed, 278 insertions, 19 deletions
diff --git a/changelog.d/3170.change.rst b/changelog.d/3170.change.rst
new file mode 100644
index 00000000..8e356ca3
--- /dev/null
+++ b/changelog.d/3170.change.rst
@@ -0,0 +1 @@
+Adopt nspektr (vendored) to implement Distribution._install_dependencies.
diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER b/setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE b/setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 00000000..aadc3749
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA
@@ -0,0 +1,57 @@
+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
new file mode 100644
index 00000000..5e5de5eb
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD
@@ -0,0 +1,11 @@
+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
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/REQUESTED
diff --git a/setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL b/setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 00000000..b10ef50a
--- /dev/null
+++ b/setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+nspektr
diff --git a/setuptools/_vendor/nspektr/__init__.py b/setuptools/_vendor/nspektr/__init__.py
new file mode 100644
index 00000000..938bbdb9
--- /dev/null
+++ b/setuptools/_vendor/nspektr/__init__.py
@@ -0,0 +1,145 @@
+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
new file mode 100644
index 00000000..3278379a
--- /dev/null
+++ b/setuptools/_vendor/nspektr/_compat.py
@@ -0,0 +1,21 @@
+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 db24b402..4320b352 100644
--- a/setuptools/_vendor/vendored.txt
+++ b/setuptools/_vendor/vendored.txt
@@ -5,6 +5,7 @@ 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/dist.py b/setuptools/dist.py
index e825785e..b55996f0 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -28,7 +28,8 @@ from distutils.util import rfc822_escape
from setuptools.extern import packaging
from setuptools.extern import ordered_set
-from setuptools.extern.more_itertools import unique_everseen, always_iterable
+from setuptools.extern.more_itertools import unique_everseen
+from setuptools.extern import nspektr
from ._importlib import metadata
@@ -40,7 +41,7 @@ from setuptools import windows_support
from setuptools.monkey import get_unpatched
from setuptools.config import parse_configuration
import pkg_resources
-from setuptools.extern.packaging import version, requirements
+from setuptools.extern.packaging import version
from . import _reqs
from . import _entry_points
@@ -876,25 +877,10 @@ class Distribution(_Distribution):
Given an entry point, ensure that any declared extras for
its distribution are installed.
"""
- reqs = {
- req
- for req in map(requirements.Requirement, always_iterable(ep.dist.requires))
- for extra in ep.extras
- if extra in req.extras
- }
- missing = itertools.filterfalse(self._is_installed, reqs)
- for req in missing:
+ for req in nspektr.missing(ep):
# fetch_build_egg expects pkg_resources.Requirement
self.fetch_build_egg(pkg_resources.Requirement(str(req)))
- def _is_installed(self, req):
- try:
- dist = metadata.distribution(req.name)
- except metadata.PackageNotFoundError:
- return False
- found_ver = packaging.version.Version(dist.version())
- return found_ver in req.specifier
-
def get_egg_cache_dir(self):
egg_cache_dir = os.path.join(os.curdir, '.eggs')
if not os.path.exists(egg_cache_dir):
diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py
index 98235a4b..7907fafc 100644
--- a/setuptools/extern/__init__.py
+++ b/setuptools/extern/__init__.py
@@ -71,6 +71,6 @@ class VendorImporter:
names = (
'packaging', 'pyparsing', 'ordered_set', 'more_itertools', 'importlib_metadata',
- 'zipp', 'importlib_resources', 'jaraco', 'typing_extensions',
+ 'zipp', 'importlib_resources', 'jaraco', 'typing_extensions', 'nspektr',
)
VendorImporter(__name__, names, 'setuptools._vendor').install()
diff --git a/tools/vendored.py b/tools/vendored.py
index 8a122ad7..cd15adbf 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -89,6 +89,16 @@ 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
@@ -133,6 +143,7 @@ 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()