bpo-38086: Sync importlib.metadata with importlib_metadata 0.21. (GH-15840)

https://gitlab.com/python-devs/importlib_metadata/-/tags/0.21
diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py
index 3b46142..e230766 100644
--- a/Lib/importlib/metadata.py
+++ b/Lib/importlib/metadata.py
@@ -19,6 +19,7 @@
 
 __all__ = [
     'Distribution',
+    'DistributionFinder',
     'PackageNotFoundError',
     'distribution',
     'distributions',
@@ -158,7 +159,7 @@
             metadata cannot be found.
         """
         for resolver in cls._discover_resolvers():
-            dists = resolver(name)
+            dists = resolver(DistributionFinder.Context(name=name))
             dist = next(dists, None)
             if dist is not None:
                 return dist
@@ -166,17 +167,34 @@
             raise PackageNotFoundError(name)
 
     @classmethod
-    def discover(cls):
+    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 all packages.
         """
+        context = kwargs.pop('context', None)
+        if context and kwargs:
+            raise ValueError("cannot accept context and kwargs")
+        context = context or DistributionFinder.Context(**kwargs)
         return itertools.chain.from_iterable(
-            resolver()
+            resolver(context)
             for resolver in cls._discover_resolvers()
             )
 
     @staticmethod
+    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
+        """
+        return PathDistribution(pathlib.Path(path))
+
+    @staticmethod
     def _discover_resolvers():
         """Search the meta_path for resolvers."""
         declared = (
@@ -215,7 +233,7 @@
     def files(self):
         """Files in this distribution.
 
-        :return: Iterable of PackagePath for this distribution or None
+        :return: List of PackagePath for this distribution or None
 
         Result is `None` if the metadata file that enumerates files
         (i.e. RECORD for dist-info or SOURCES.txt for egg-info) is
@@ -231,7 +249,7 @@
             result.dist = self
             return result
 
-        return file_lines and starmap(make_file, csv.reader(file_lines))
+        return file_lines and list(starmap(make_file, csv.reader(file_lines)))
 
     def _read_files_distinfo(self):
         """
@@ -251,7 +269,8 @@
     @property
     def requires(self):
         """Generated requirements specified for this Distribution"""
-        return self._read_dist_info_reqs() or self._read_egg_info_reqs()
+        reqs = self._read_dist_info_reqs() or self._read_egg_info_reqs()
+        return reqs and list(reqs)
 
     def _read_dist_info_reqs(self):
         return self.metadata.get_all('Requires-Dist')
@@ -312,15 +331,35 @@
     A MetaPathFinder capable of discovering installed distributions.
     """
 
+    class Context:
+
+        name = None
+        """
+        Specific name for which a distribution finder should match.
+        """
+
+        def __init__(self, **kwargs):
+            vars(self).update(kwargs)
+
+        @property
+        def path(self):
+            """
+            The path that a distribution finder should search.
+            """
+            return vars(self).get('path', sys.path)
+
+        @property
+        def pattern(self):
+            return '.*' if self.name is None else re.escape(self.name)
+
     @abc.abstractmethod
-    def find_distributions(self, name=None, path=None):
+    def find_distributions(self, context=Context()):
         """
         Find distributions.
 
         Return an iterable of all Distribution instances capable of
-        loading the metadata for packages matching the ``name``
-        (or all names if not supplied) along the paths in the list
-        of directories ``path`` (defaults to sys.path).
+        loading the metadata for packages matching the ``context``,
+        a DistributionFinder.Context instance.
         """
 
 
@@ -352,12 +391,12 @@
     return Distribution.from_name(package)
 
 
-def distributions():
+def distributions(**kwargs):
     """Get all ``Distribution`` instances in the current environment.
 
     :return: An iterable of ``Distribution`` instances.
     """
-    return Distribution.discover()
+    return Distribution.discover(**kwargs)
 
 
 def metadata(package):