From db438fa6921f5d7bb45f11eaf6ddd04c3eb8d505 Mon Sep 17 00:00:00 2001
From: Matt Davis <matteius@gmail.com>
Date: Wed, 24 Apr 2024 01:08:37 -0400
Subject: [PATCH] Go with the version of importlib-metadata that targets the
 3.11 sdlib

---
 pipenv/vendor/importlib_metadata/__init__.py  | 334 ++++++------------
 pipenv/vendor/importlib_metadata/_adapters.py |   2 +-
 pipenv/vendor/importlib_metadata/_compat.py   |  19 +-
 pipenv/vendor/importlib_metadata/_meta.py     |  54 ++-
 .../{compat/py39.py => _py39compat.py}        |   7 +-
 .../importlib_metadata/compat/__init__.py     |   0
 pipenv/vendor/importlib_metadata/diagnose.py  |  21 --
 pipenv/vendor/vendor.txt                      |   2 +-
 8 files changed, 151 insertions(+), 288 deletions(-)
 rename pipenv/vendor/importlib_metadata/{compat/py39.py => _py39compat.py} (82%)
 delete mode 100644 pipenv/vendor/importlib_metadata/compat/__init__.py
 delete mode 100644 pipenv/vendor/importlib_metadata/diagnose.py

diff --git a/pipenv/vendor/importlib_metadata/__init__.py b/pipenv/vendor/importlib_metadata/__init__.py
index 1ab98225ab..fdc765ccbf 100644
--- a/pipenv/vendor/importlib_metadata/__init__.py
+++ b/pipenv/vendor/importlib_metadata/__init__.py
@@ -1,14 +1,10 @@
-from __future__ import annotations
-
 import os
 import re
 import abc
+import csv
 import sys
-import json
 import pipenv.vendor.zipp as zipp
 import email
-import types
-import inspect
 import pathlib
 import operator
 import textwrap
@@ -16,14 +12,16 @@
 import functools
 import itertools
 import posixpath
+import contextlib
 import collections
+import inspect
 
-from . import _adapters, _meta
-from .compat import py39
+from . import _adapters, _meta, _py39compat
 from ._collections import FreezableDefaultDict, Pair
 from ._compat import (
     NullFinder,
     install,
+    pypy_partial,
 )
 from ._functools import method_cache, pass_none
 from ._itertools import always_iterable, unique_everseen
@@ -33,7 +31,8 @@
 from importlib import import_module
 from importlib.abc import MetaPathFinder
 from itertools import starmap
-from typing import Any, Iterable, List, Mapping, Match, Optional, Set, cast
+from typing import List, Mapping, Optional, cast
+
 
 __all__ = [
     'Distribution',
@@ -54,11 +53,11 @@
 class PackageNotFoundError(ModuleNotFoundError):
     """The package was not found."""
 
-    def __str__(self) -> str:
+    def __str__(self):
         return f"No package metadata was found for {self.name}"
 
     @property
-    def name(self) -> str:  # type: ignore[override]
+    def name(self):
         (name,) = self.args
         return name
 
@@ -124,11 +123,38 @@ def read(text, filter_=None):
             yield Pair(name, value)
 
     @staticmethod
-    def valid(line: str):
+    def valid(line):
         return line and not line.startswith('#')
 
 
-class EntryPoint:
+class DeprecatedTuple:
+    """
+    Provide subscript item access for backward compatibility.
+
+    >>> recwarn = getfixture('recwarn')
+    >>> ep = EntryPoint(name='name', value='value', group='group')
+    >>> ep[:]
+    ('name', 'value', 'group')
+    >>> ep[0]
+    'name'
+    >>> len(recwarn)
+    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.",
+        DeprecationWarning,
+        stacklevel=pypy_partial(2),
+    )
+
+    def __getitem__(self, item):
+        self._warn()
+        return self._key()[item]
+
+
+class EntryPoint(DeprecatedTuple):
     """An entry point as defined by Python packaging conventions.
 
     See `the packaging docs on entry points
@@ -170,37 +196,34 @@ class EntryPoint:
     value: str
     group: str
 
-    dist: Optional[Distribution] = None
+    dist: Optional['Distribution'] = None
 
-    def __init__(self, name: str, value: str, group: str) -> None:
+    def __init__(self, name, value, group):
         vars(self).update(name=name, value=value, group=group)
 
-    def load(self) -> Any:
+    def load(self):
         """Load the entry point from its definition. If only a module
         is indicated by the value, return that module. Otherwise,
         return the named object.
         """
-        match = cast(Match, self.pattern.match(self.value))
+        match = self.pattern.match(self.value)
         module = import_module(match.group('module'))
         attrs = filter(None, (match.group('attr') or '').split('.'))
         return functools.reduce(getattr, attrs, module)
 
     @property
-    def module(self) -> str:
+    def module(self):
         match = self.pattern.match(self.value)
-        assert match is not None
         return match.group('module')
 
     @property
-    def attr(self) -> str:
+    def attr(self):
         match = self.pattern.match(self.value)
-        assert match is not None
         return match.group('attr')
 
     @property
-    def extras(self) -> List[str]:
+    def extras(self):
         match = self.pattern.match(self.value)
-        assert match is not None
         return re.findall(r'\w+', match.group('extras') or '')
 
     def _for(self, dist):
@@ -248,7 +271,7 @@ def __repr__(self):
             f'group={self.group!r})'
         )
 
-    def __hash__(self) -> int:
+    def __hash__(self):
         return hash(self._key())
 
 
@@ -259,7 +282,7 @@ class EntryPoints(tuple):
 
     __slots__ = ()
 
-    def __getitem__(self, name: str) -> EntryPoint:  # type: ignore[override]
+    def __getitem__(self, name):  # -> EntryPoint:
         """
         Get the EntryPoint in self matching name.
         """
@@ -268,29 +291,22 @@ def __getitem__(self, name: str) -> EntryPoint:  # type: ignore[override]
         except StopIteration:
             raise KeyError(name)
 
-    def __repr__(self):
-        """
-        Repr with classname and tuple constructor to
-        signal that we deviate from regular tuple behavior.
-        """
-        return '%s(%r)' % (self.__class__.__name__, tuple(self))
-
-    def select(self, **params) -> EntryPoints:
+    def select(self, **params):
         """
         Select entry points from self that match the
         given parameters (typically group and/or name).
         """
-        return EntryPoints(ep for ep in self if py39.ep_matches(ep, **params))
+        return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params))
 
     @property
-    def names(self) -> Set[str]:
+    def names(self):
         """
         Return the set of all names of all entry points.
         """
         return {ep.name for ep in self}
 
     @property
-    def groups(self) -> Set[str]:
+    def groups(self):
         """
         Return the set of all groups of all entry points.
         """
@@ -311,31 +327,28 @@ def _from_text(text):
 class PackagePath(pathlib.PurePosixPath):
     """A reference to a path in a package"""
 
-    hash: Optional[FileHash]
-    size: int
-    dist: Distribution
-
-    def read_text(self, encoding: str = 'utf-8') -> str:  # type: ignore[override]
-        return self.locate().read_text(encoding=encoding)
+    def read_text(self, encoding='utf-8'):
+        with self.locate().open(encoding=encoding) as stream:
+            return stream.read()
 
-    def read_binary(self) -> bytes:
-        return self.locate().read_bytes()
+    def read_binary(self):
+        with self.locate().open('rb') as stream:
+            return stream.read()
 
-    def locate(self) -> SimplePath:
+    def locate(self):
         """Return a path-like object for this path"""
         return self.dist.locate_file(self)
 
 
 class FileHash:
-    def __init__(self, spec: str) -> None:
+    def __init__(self, spec):
         self.mode, _, self.value = spec.partition('=')
 
-    def __repr__(self) -> str:
+    def __repr__(self):
         return f'<FileHash mode: {self.mode} value: {self.value}>'
 
 
 class DeprecatedNonAbstract:
-    # Required until Python 3.14
     def __new__(cls, *args, **kwargs):
         all_names = {
             name for subclass in inspect.getmro(cls) for name in vars(subclass)
@@ -355,48 +368,25 @@ def __new__(cls, *args, **kwargs):
 
 
 class Distribution(DeprecatedNonAbstract):
-    """
-    An abstract Python distribution package.
-
-    Custom providers may derive from this class and define
-    the abstract methods to provide a concrete implementation
-    for their environment. Some providers may opt to override
-    the default implementation of some properties to bypass
-    the file-reading mechanism.
-    """
+    """A Python distribution package."""
 
     @abc.abstractmethod
     def read_text(self, filename) -> Optional[str]:
         """Attempt to load metadata file given by the name.
 
-        Python distribution metadata is organized by blobs of text
-        typically represented as "files" in the metadata directory
-        (e.g. package-1.0.dist-info). These files include things
-        like:
-
-        - METADATA: The distribution metadata including fields
-          like Name and Version and Description.
-        - entry_points.txt: A series of entry points as defined in
-          `the entry points spec <https://packaging.python.org/en/latest/specifications/entry-points/#file-format>`_.
-        - RECORD: A record of files according to
-          `this recording spec <https://packaging.python.org/en/latest/specifications/recording-installed-packages/#the-record-file>`_.
-
-        A package may provide any set of files, including those
-        not listed here or none at all.
-
         :param filename: The name of the file in the distribution info.
         :return: The text if found, otherwise None.
         """
 
     @abc.abstractmethod
-    def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
+    def locate_file(self, path):
         """
-        Given a path to a file in this distribution, return a SimplePath
+        Given a path to a file in this distribution, return a path
         to it.
         """
 
     @classmethod
-    def from_name(cls, name: str) -> Distribution:
+    def from_name(cls, name: str):
         """Return the Distribution for the given package name.
 
         :param name: The name of the distribution package to search for.
@@ -409,23 +399,21 @@ def from_name(cls, name: str) -> Distribution:
         if not name:
             raise ValueError("A distribution name is required.")
         try:
-            return next(iter(cls.discover(name=name)))
+            return next(cls.discover(name=name))
         except StopIteration:
             raise PackageNotFoundError(name)
 
     @classmethod
-    def discover(
-        cls, *, context: Optional[DistributionFinder.Context] = None, **kwargs
-    ) -> Iterable[Distribution]:
+    def discover(cls, **kwargs):
         """Return an iterable of Distribution objects for all packages.
 
         Pass a ``context`` or pass keyword arguments for constructing
         a context.
 
         :context: A ``DistributionFinder.Context`` object.
-        :return: Iterable of Distribution objects for packages matching
-          the context.
+        :return: Iterable of Distribution objects for all packages.
         """
+        context = kwargs.pop('context', None)
         if context and kwargs:
             raise ValueError("cannot accept context and kwargs")
         context = context or DistributionFinder.Context(**kwargs)
@@ -434,8 +422,8 @@ def discover(
         )
 
     @staticmethod
-    def at(path: str | os.PathLike[str]) -> Distribution:
-        """Return a Distribution for the indicated metadata path.
+    def at(path):
+        """Return a Distribution for the indicated metadata path
 
         :param path: a string or path-like object
         :return: a concrete Distribution instance for the path
@@ -444,7 +432,7 @@ def at(path: str | os.PathLike[str]) -> Distribution:
 
     @staticmethod
     def _discover_resolvers():
-        """Search the meta_path for resolvers (MetadataPathFinders)."""
+        """Search the meta_path for resolvers."""
         declared = (
             getattr(finder, 'find_distributions', None) for finder in sys.meta_path
         )
@@ -455,11 +443,7 @@ def metadata(self) -> _meta.PackageMetadata:
         """Return the parsed metadata for this Distribution.
 
         The returned object will have keys that name the various bits of
-        metadata per the
-        `Core metadata specifications <https://packaging.python.org/en/latest/specifications/core-metadata/#core-metadata>`_.
-
-        Custom providers may provide the METADATA file or override this
-        property.
+        metadata.  See PEP 566 for details.
         """
         opt_text = (
             self.read_text('METADATA')
@@ -473,7 +457,7 @@ def metadata(self) -> _meta.PackageMetadata:
         return _adapters.Message(email.message_from_string(text))
 
     @property
-    def name(self) -> str:
+    def name(self):
         """Return the 'Name' metadata for the distribution package."""
         return self.metadata['Name']
 
@@ -483,22 +467,16 @@ def _normalized_name(self):
         return Prepared.normalize(self.name)
 
     @property
-    def version(self) -> str:
+    def version(self):
         """Return the 'Version' metadata for the distribution package."""
         return self.metadata['Version']
 
     @property
-    def entry_points(self) -> EntryPoints:
-        """
-        Return EntryPoints for this distribution.
-
-        Custom providers may provide the ``entry_points.txt`` file
-        or override this property.
-        """
+    def entry_points(self):
         return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self)
 
     @property
-    def files(self) -> Optional[List[PackagePath]]:
+    def files(self):
         """Files in this distribution.
 
         :return: List of PackagePath for this distribution or None
@@ -507,10 +485,6 @@ def files(self) -> Optional[List[PackagePath]]:
         (i.e. RECORD for dist-info, or installed-files.txt or
         SOURCES.txt for egg-info) is missing.
         Result may be empty if the metadata exists but is empty.
-
-        Custom providers are recommended to provide a "RECORD" file (in
-        ``read_text``) or override this property to allow for callers to be
-        able to resolve filenames provided by the package.
         """
 
         def make_file(name, hash=None, size_str=None):
@@ -522,10 +496,6 @@ def make_file(name, hash=None, size_str=None):
 
         @pass_none
         def make_files(lines):
-            # Delay csv import, since Distribution.files is not as widely used
-            # as other parts of importlib.metadata
-            import csv
-
             return starmap(make_file, csv.reader(lines))
 
         @pass_none
@@ -542,7 +512,7 @@ def skip_missing_files(package_paths):
 
     def _read_files_distinfo(self):
         """
-        Read the lines of RECORD.
+        Read the lines of RECORD
         """
         text = self.read_text('RECORD')
         return text and text.splitlines()
@@ -591,7 +561,7 @@ def _read_files_egginfo_sources(self):
         return text and map('"{}"'.format, text.splitlines())
 
     @property
-    def requires(self) -> Optional[List[str]]:
+    def requires(self):
         """Generated requirements specified for this Distribution"""
         reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
         return reqs and list(reqs)
@@ -642,23 +612,10 @@ def url_req_space(req):
             space = url_req_space(section.value)
             yield section.value + space + quoted_marker(section.name)
 
-    @property
-    def origin(self):
-        return self._load_json('direct_url.json')
-
-    def _load_json(self, filename):
-        return pass_none(json.loads)(
-            self.read_text(filename),
-            object_hook=lambda data: types.SimpleNamespace(**data),
-        )
-
 
 class DistributionFinder(MetaPathFinder):
     """
     A MetaPathFinder capable of discovering installed distributions.
-
-    Custom providers should implement this interface in order to
-    supply metadata.
     """
 
     class Context:
@@ -671,17 +628,6 @@ class Context:
         Each DistributionFinder may expect any parameters
         and should attempt to honor the canonical
         parameters defined below when appropriate.
-
-        This mechanism gives a custom provider a means to
-        solicit additional details from the caller beyond
-        "name" and "path" when searching distributions.
-        For example, imagine a provider that exposes suites
-        of packages in either a "public" or "private" ``realm``.
-        A caller may wish to query only for distributions in
-        a particular realm and could call
-        ``distributions(realm="private")`` to signal to the
-        custom provider to only include distributions from that
-        realm.
         """
 
         name = None
@@ -694,7 +640,7 @@ def __init__(self, **kwargs):
             vars(self).update(kwargs)
 
         @property
-        def path(self) -> List[str]:
+        def path(self):
             """
             The sequence of directory path that a distribution finder
             should search.
@@ -705,7 +651,7 @@ def path(self) -> List[str]:
             return vars(self).get('path', sys.path)
 
     @abc.abstractmethod
-    def find_distributions(self, context=Context()) -> Iterable[Distribution]:
+    def find_distributions(self, context=Context()):
         """
         Find distributions.
 
@@ -717,18 +663,11 @@ def find_distributions(self, context=Context()) -> Iterable[Distribution]:
 
 class FastPath:
     """
-    Micro-optimized class for searching a root for children.
-
-    Root is a path on the file system that may contain metadata
-    directories either as natural directories or within a zip file.
+    Micro-optimized class for searching a path for
+    children.
 
     >>> FastPath('').children()
     ['...']
-
-    FastPath objects are cached and recycled for any given root.
-
-    >>> FastPath('foobar') is FastPath('foobar')
-    True
     """
 
     @functools.lru_cache()  # type: ignore
@@ -770,19 +709,7 @@ def lookup(self, mtime):
 
 
 class Lookup:
-    """
-    A micro-optimized class for searching a (fast) path for metadata.
-    """
-
     def __init__(self, path: FastPath):
-        """
-        Calculate all of the children representing metadata.
-
-        From the children in the path, calculate early all of the
-        children that appear to represent metadata (infos) or legacy
-        metadata (eggs).
-        """
-
         base = os.path.basename(path.root).lower()
         base_is_egg = base.endswith(".egg")
         self.infos = FreezableDefaultDict(list)
@@ -803,10 +730,7 @@ def __init__(self, path: FastPath):
         self.infos.freeze()
         self.eggs.freeze()
 
-    def search(self, prepared: Prepared):
-        """
-        Yield all infos and eggs matching the Prepared query.
-        """
+    def search(self, prepared):
         infos = (
             self.infos[prepared.normalized]
             if prepared
@@ -822,28 +746,13 @@ def search(self, prepared: Prepared):
 
 class Prepared:
     """
-    A prepared search query for metadata on a possibly-named package.
-
-    Pre-calculates the normalization to prevent repeated operations.
-
-    >>> none = Prepared(None)
-    >>> none.normalized
-    >>> none.legacy_normalized
-    >>> bool(none)
-    False
-    >>> sample = Prepared('Sample__Pkg-name.foo')
-    >>> sample.normalized
-    'sample_pkg_name_foo'
-    >>> sample.legacy_normalized
-    'sample__pkg_name.foo'
-    >>> bool(sample)
-    True
+    A prepared search for metadata on a possibly-named package.
     """
 
     normalized = None
     legacy_normalized = None
 
-    def __init__(self, name: Optional[str]):
+    def __init__(self, name):
         self.name = name
         if name is None:
             return
@@ -877,10 +786,7 @@ class MetadataPathFinder(NullFinder, DistributionFinder):
     of Python that do not have a PathFinder find_distributions().
     """
 
-    @classmethod
-    def find_distributions(
-        cls, context=DistributionFinder.Context()
-    ) -> Iterable[PathDistribution]:
+    def find_distributions(self, context=DistributionFinder.Context()):
         """
         Find distributions.
 
@@ -889,7 +795,7 @@ def find_distributions(
         (or all names if ``None`` indicated) along the paths in the list
         of directories ``context.path``.
         """
-        found = cls._search_paths(context.name, context.path)
+        found = self._search_paths(context.name, context.path)
         return map(PathDistribution, found)
 
     @classmethod
@@ -900,20 +806,19 @@ def _search_paths(cls, name, paths):
             path.search(prepared) for path in map(FastPath, paths)
         )
 
-    @classmethod
-    def invalidate_caches(cls) -> None:
+    def invalidate_caches(cls):
         FastPath.__new__.cache_clear()
 
 
 class PathDistribution(Distribution):
-    def __init__(self, path: SimplePath) -> None:
+    def __init__(self, path: SimplePath):
         """Construct a distribution.
 
         :param path: SimplePath indicating the metadata directory.
         """
         self._path = path
 
-    def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]:
+    def read_text(self, filename):
         with suppress(
             FileNotFoundError,
             IsADirectoryError,
@@ -923,11 +828,9 @@ def read_text(self, filename: str | os.PathLike[str]) -> Optional[str]:
         ):
             return self._path.joinpath(filename).read_text(encoding='utf-8')
 
-        return None
-
     read_text.__doc__ = Distribution.read_text.__doc__
 
-    def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
+    def locate_file(self, path):
         return self._path.parent / path
 
     @property
@@ -960,7 +863,7 @@ def _name_from_stem(stem):
         return name
 
 
-def distribution(distribution_name: str) -> Distribution:
+def distribution(distribution_name):
     """Get the ``Distribution`` instance for the named package.
 
     :param distribution_name: The name of the distribution package as a string.
@@ -969,7 +872,7 @@ def distribution(distribution_name: str) -> Distribution:
     return Distribution.from_name(distribution_name)
 
 
-def distributions(**kwargs) -> Iterable[Distribution]:
+def distributions(**kwargs):
     """Get all ``Distribution`` instances in the current environment.
 
     :return: An iterable of ``Distribution`` instances.
@@ -977,7 +880,7 @@ def distributions(**kwargs) -> Iterable[Distribution]:
     return Distribution.discover(**kwargs)
 
 
-def metadata(distribution_name: str) -> _meta.PackageMetadata:
+def metadata(distribution_name) -> _meta.PackageMetadata:
     """Get the metadata for the named package.
 
     :param distribution_name: The name of the distribution package to query.
@@ -986,7 +889,7 @@ def metadata(distribution_name: str) -> _meta.PackageMetadata:
     return Distribution.from_name(distribution_name).metadata
 
 
-def version(distribution_name: str) -> str:
+def version(distribution_name):
     """Get the version string for the named package.
 
     :param distribution_name: The name of the distribution package to query.
@@ -998,7 +901,7 @@ def version(distribution_name: str) -> str:
 
 _unique = functools.partial(
     unique_everseen,
-    key=py39.normalized_name,
+    key=_py39compat.normalized_name,
 )
 """
 Wrapper for ``distributions`` to return unique distributions by name.
@@ -1020,7 +923,7 @@ def entry_points(**params) -> EntryPoints:
     return EntryPoints(eps).select(**params)
 
 
-def files(distribution_name: str) -> Optional[List[PackagePath]]:
+def files(distribution_name):
     """Return a list of files for the named package.
 
     :param distribution_name: The name of the distribution package to query.
@@ -1029,11 +932,11 @@ def files(distribution_name: str) -> Optional[List[PackagePath]]:
     return distribution(distribution_name).files
 
 
-def requires(distribution_name: str) -> Optional[List[str]]:
+def requires(distribution_name):
     """
     Return a list of requirements for the named package.
 
-    :return: An iterable of requirements, suitable for
+    :return: An iterator of requirements, suitable for
         packaging.requirement.Requirement.
     """
     return distribution(distribution_name).requires
@@ -1060,42 +963,13 @@ def _top_level_declared(dist):
     return (dist.read_text('top_level.txt') or '').split()
 
 
-def _topmost(name: PackagePath) -> Optional[str]:
-    """
-    Return the top-most parent as long as there is a parent.
-    """
-    top, *rest = name.parts
-    return top if rest else None
-
-
-def _get_toplevel_name(name: PackagePath) -> str:
-    """
-    Infer a possibly importable module name from a name presumed on
-    sys.path.
-
-    >>> _get_toplevel_name(PackagePath('foo.py'))
-    'foo'
-    >>> _get_toplevel_name(PackagePath('foo'))
-    'foo'
-    >>> _get_toplevel_name(PackagePath('foo.pyc'))
-    'foo'
-    >>> _get_toplevel_name(PackagePath('foo/__init__.py'))
-    'foo'
-    >>> _get_toplevel_name(PackagePath('foo.pth'))
-    'foo.pth'
-    >>> _get_toplevel_name(PackagePath('foo.dist-info'))
-    'foo.dist-info'
-    """
-    return _topmost(name) or (
-        # python/typeshed#10328
-        inspect.getmodulename(name)  # type: ignore
-        or str(name)
-    )
-
-
 def _top_level_inferred(dist):
-    opt_names = set(map(_get_toplevel_name, always_iterable(dist.files)))
+    opt_names = {
+        f.parts[0] if len(f.parts) > 1 else inspect.getmodulename(f)
+        for f in always_iterable(dist.files)
+    }
 
+    @pass_none
     def importable_name(name):
         return '.' not in name
 
diff --git a/pipenv/vendor/importlib_metadata/_adapters.py b/pipenv/vendor/importlib_metadata/_adapters.py
index 120e43a048..e33cba5e44 100644
--- a/pipenv/vendor/importlib_metadata/_adapters.py
+++ b/pipenv/vendor/importlib_metadata/_adapters.py
@@ -54,7 +54,7 @@ def __iter__(self):
     def __getitem__(self, item):
         """
         Warn users that a ``KeyError`` can be expected when a
-        missing key is supplied. Ref python/importlib_metadata#371.
+        mising key is supplied. Ref python/importlib_metadata#371.
         """
         res = super().__getitem__(item)
         if res is None:
diff --git a/pipenv/vendor/importlib_metadata/_compat.py b/pipenv/vendor/importlib_metadata/_compat.py
index df312b1cbb..cdb1352c45 100644
--- a/pipenv/vendor/importlib_metadata/_compat.py
+++ b/pipenv/vendor/importlib_metadata/_compat.py
@@ -2,7 +2,14 @@
 import platform
 
 
-__all__ = ['install', 'NullFinder']
+__all__ = ['install', 'NullFinder', 'Protocol']
+
+
+try:
+    from typing import Protocol
+except ImportError:  # pragma: no cover
+    # Python 3.7 compatibility
+    from pipenv.patched.pip._vendor.typing_extensions import Protocol  # type: ignore
 
 
 def install(cls):
@@ -38,7 +45,7 @@ def matches(finder):
 
 class NullFinder:
     """
-    A "Finder" (aka "MetaPathFinder") that never finds any modules,
+    A "Finder" (aka "MetaClassFinder") that never finds any modules,
     but may find distributions.
     """
 
@@ -46,6 +53,14 @@ class NullFinder:
     def find_spec(*args, **kwargs):
         return None
 
+    # In Python 2, the import system requires finders
+    # to have a find_module() method, but this usage
+    # is deprecated in Python 3 in favor of find_spec().
+    # For the purposes of this finder (i.e. being present
+    # on sys.meta_path but having no other import
+    # system functionality), the two methods are identical.
+    find_module = find_spec
+
 
 def pypy_partial(val):
     """
diff --git a/pipenv/vendor/importlib_metadata/_meta.py b/pipenv/vendor/importlib_metadata/_meta.py
index 1927d0f624..e27d34aabd 100644
--- a/pipenv/vendor/importlib_metadata/_meta.py
+++ b/pipenv/vendor/importlib_metadata/_meta.py
@@ -1,7 +1,4 @@
-from __future__ import annotations
-
-import os
-from typing import Protocol
+from ._compat import Protocol
 from typing import Any, Dict, Iterator, List, Optional, TypeVar, Union, overload
 
 
@@ -9,27 +6,30 @@
 
 
 class PackageMetadata(Protocol):
-    def __len__(self) -> int: ...  # pragma: no cover
+    def __len__(self) -> int:
+        ...  # pragma: no cover
 
-    def __contains__(self, item: str) -> bool: ...  # pragma: no cover
+    def __contains__(self, item: str) -> bool:
+        ...  # pragma: no cover
 
-    def __getitem__(self, key: str) -> str: ...  # pragma: no cover
+    def __getitem__(self, key: str) -> str:
+        ...  # pragma: no cover
 
-    def __iter__(self) -> Iterator[str]: ...  # pragma: no cover
+    def __iter__(self) -> Iterator[str]:
+        ...  # pragma: no cover
 
     @overload
-    def get(
-        self, name: str, failobj: None = None
-    ) -> Optional[str]: ...  # pragma: no cover
+    def get(self, name: str, failobj: None = None) -> Optional[str]:
+        ...  # pragma: no cover
 
     @overload
-    def get(self, name: str, failobj: _T) -> Union[str, _T]: ...  # pragma: no cover
+    def get(self, name: str, failobj: _T) -> Union[str, _T]:
+        ...  # pragma: no cover
 
     # overload per python/importlib_metadata#435
     @overload
-    def get_all(
-        self, name: str, failobj: None = None
-    ) -> Optional[List[Any]]: ...  # pragma: no cover
+    def get_all(self, name: str, failobj: None = None) -> Optional[List[Any]]:
+        ...  # pragma: no cover
 
     @overload
     def get_all(self, name: str, failobj: _T) -> Union[List[Any], _T]:
@@ -44,24 +44,20 @@ def json(self) -> Dict[str, Union[str, List[str]]]:
         """
 
 
-class SimplePath(Protocol):
+class SimplePath(Protocol[_T]):
     """
-    A minimal subset of pathlib.Path required by Distribution.
+    A minimal subset of pathlib.Path required by PathDistribution.
     """
 
-    def joinpath(
-        self, other: Union[str, os.PathLike[str]]
-    ) -> SimplePath: ...  # pragma: no cover
+    def joinpath(self) -> _T:
+        ...  # pragma: no cover
 
-    def __truediv__(
-        self, other: Union[str, os.PathLike[str]]
-    ) -> SimplePath: ...  # pragma: no cover
+    def __truediv__(self, other: Union[str, _T]) -> _T:
+        ...  # pragma: no cover
 
     @property
-    def parent(self) -> SimplePath: ...  # pragma: no cover
-
-    def read_text(self, encoding=None) -> str: ...  # pragma: no cover
-
-    def read_bytes(self) -> bytes: ...  # pragma: no cover
+    def parent(self) -> _T:
+        ...  # pragma: no cover
 
-    def exists(self) -> bool: ...  # pragma: no cover
+    def read_text(self) -> str:
+        ...  # pragma: no cover
diff --git a/pipenv/vendor/importlib_metadata/compat/py39.py b/pipenv/vendor/importlib_metadata/_py39compat.py
similarity index 82%
rename from pipenv/vendor/importlib_metadata/compat/py39.py
rename to pipenv/vendor/importlib_metadata/_py39compat.py
index 1f15bd97e6..cde4558fbb 100644
--- a/pipenv/vendor/importlib_metadata/compat/py39.py
+++ b/pipenv/vendor/importlib_metadata/_py39compat.py
@@ -1,12 +1,11 @@
 """
 Compatibility layer with Python 3.8/3.9
 """
-
 from typing import TYPE_CHECKING, Any, Optional
 
 if TYPE_CHECKING:  # pragma: no cover
     # Prevent circular imports on runtime.
-    from .. import Distribution, EntryPoint
+    from . import Distribution, EntryPoint
 else:
     Distribution = EntryPoint = Any
 
@@ -18,7 +17,7 @@ def normalized_name(dist: Distribution) -> Optional[str]:
     try:
         return dist._normalized_name
     except AttributeError:
-        from .. import Prepared  # -> delay to prevent circular imports.
+        from . import Prepared  # -> delay to prevent circular imports.
 
         return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name'])
 
@@ -30,7 +29,7 @@ def ep_matches(ep: EntryPoint, **params) -> bool:
     try:
         return ep.matches(**params)
     except AttributeError:
-        from .. import EntryPoint  # -> delay to prevent circular imports.
+        from . import EntryPoint  # -> delay to prevent circular imports.
 
         # Reconstruct the EntryPoint object to make sure it is compatible.
         return EntryPoint(ep.name, ep.value, ep.group).matches(**params)
diff --git a/pipenv/vendor/importlib_metadata/compat/__init__.py b/pipenv/vendor/importlib_metadata/compat/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/pipenv/vendor/importlib_metadata/diagnose.py b/pipenv/vendor/importlib_metadata/diagnose.py
deleted file mode 100644
index e405471ac4..0000000000
--- a/pipenv/vendor/importlib_metadata/diagnose.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import sys
-
-from . import Distribution
-
-
-def inspect(path):
-    print("Inspecting", path)
-    dists = list(Distribution.discover(path=[path]))
-    if not dists:
-        return
-    print("Found", len(dists), "packages:", end=' ')
-    print(', '.join(dist.name for dist in dists))
-
-
-def run():
-    for path in sys.path:
-        inspect(path)
-
-
-if __name__ == '__main__':
-    run()
diff --git a/pipenv/vendor/vendor.txt b/pipenv/vendor/vendor.txt
index f072cb7c38..39bd33c7ec 100644
--- a/pipenv/vendor/vendor.txt
+++ b/pipenv/vendor/vendor.txt
@@ -2,7 +2,7 @@ click-didyoumean==0.3.1
 click==8.1.7
 colorama==0.4.6
 dparse==0.6.3
-importlib-metadata==7.1.0
+importlib-metadata==6.5.1
 pexpect==4.9.0
 pipdeptree==2.18.1
 plette==0.4.4