Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/3170.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adopt nspektr (vendored) to implement Distribution._install_dependencies.
1 change: 1 addition & 0 deletions setuptools/_vendor/nspektr-0.3.0.dist-info/INSTALLER
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pip
19 changes: 19 additions & 0 deletions setuptools/_vendor/nspektr-0.3.0.dist-info/LICENSE
Original file line number Diff line number Diff line change
@@ -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.
57 changes: 57 additions & 0 deletions setuptools/_vendor/nspektr-0.3.0.dist-info/METADATA
Original file line number Diff line number Diff line change
@@ -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: [email protected]
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


11 changes: 11 additions & 0 deletions setuptools/_vendor/nspektr-0.3.0.dist-info/RECORD
Original file line number Diff line number Diff line change
@@ -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
Empty file.
5 changes: 5 additions & 0 deletions setuptools/_vendor/nspektr-0.3.0.dist-info/WHEEL
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.37.1)
Root-Is-Purelib: true
Tag: py3-none-any

1 change: 1 addition & 0 deletions setuptools/_vendor/nspektr-0.3.0.dist-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nspektr
145 changes: 145 additions & 0 deletions setuptools/_vendor/nspektr/__init__.py
Original file line number Diff line number Diff line change
@@ -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)
21 changes: 21 additions & 0 deletions setuptools/_vendor/nspektr/_compat.py
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions setuptools/_vendor/vendored.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 4 additions & 18 deletions setuptools/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@

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

Expand All @@ -40,7 +41,7 @@
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

Expand Down Expand Up @@ -876,25 +877,10 @@ def _install_dependencies(self, ep):
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):
Expand Down
2 changes: 1 addition & 1 deletion setuptools/extern/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,6 @@ def install(self):

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()
11 changes: 11 additions & 0 deletions tools/vendored.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()