]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Expand the 'Extending' docs with an example. (#113187)
authorJason R. Coombs <jaraco@jaraco.com>
Wed, 1 May 2024 17:04:02 +0000 (13:04 -0400)
committerGitHub <noreply@github.com>
Wed, 1 May 2024 17:04:02 +0000 (17:04 +0000)
* Expand the 'Extending' docs to provide a minimal example. Closes python/importlib_metadata#427.

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Doc/library/importlib.metadata.rst

index 5039bc6e85ee2065164c12689bf405f9dc258752..674ce5807fdf11e5942931ee3b44114c8a6905c3 100644 (file)
@@ -406,6 +406,84 @@ metadata in locations other than the file system, subclass
 a custom finder, return instances of this derived ``Distribution`` in the
 ``find_distributions()`` method.
 
+Example
+-------
+
+Consider for example a custom finder that loads Python
+modules from a database::
+
+    class DatabaseImporter(importlib.abc.MetaPathFinder):
+        def __init__(self, db):
+            self.db = db
+
+        def find_spec(self, fullname, target=None) -> ModuleSpec:
+            return self.db.spec_from_name(fullname)
+
+    sys.meta_path.append(DatabaseImporter(connect_db(...)))
+
+That importer now presumably provides importable modules from a
+database, but it provides no metadata or entry points. For this
+custom importer to provide metadata, it would also need to implement
+``DistributionFinder``::
+
+    from importlib.metadata import DistributionFinder
+
+    class DatabaseImporter(DistributionFinder):
+        ...
+
+        def find_distributions(self, context=DistributionFinder.Context()):
+            query = dict(name=context.name) if context.name else {}
+            for dist_record in self.db.query_distributions(query):
+                yield DatabaseDistribution(dist_record)
+
+In this way, ``query_distributions`` would return records for
+each distribution served by the database matching the query. For
+example, if ``requests-1.0`` is in the database, ``find_distributions``
+would yield a ``DatabaseDistribution`` for ``Context(name='requests')``
+or ``Context(name=None)``.
+
+For the sake of simplicity, this example ignores ``context.path``\. The
+``path`` attribute defaults to ``sys.path`` and is the set of import paths to
+be considered in the search. A ``DatabaseImporter`` could potentially function
+without any concern for a search path. Assuming the importer does no
+partitioning, the "path" would be irrelevant. In order to illustrate the
+purpose of ``path``, the example would need to illustrate a more complex
+``DatabaseImporter`` whose behavior varied depending on
+``sys.path``/``PYTHONPATH``. In that case, the ``find_distributions`` should
+honor the ``context.path`` and only yield ``Distribution``\ s pertinent to that
+path.
+
+``DatabaseDistribution``, then, would look something like::
+
+    class DatabaseDistribution(importlib.metadata.Distributon):
+        def __init__(self, record):
+            self.record = record
+
+        def read_text(self, filename):
+            """
+            Read a file like "METADATA" for the current distribution.
+            """
+            if filename == "METADATA":
+                return f"""Name: {self.record.name}
+    Version: {self.record.version}
+    """
+            if filename == "entry_points.txt":
+                return "\n".join(
+                  f"""[{ep.group}]\n{ep.name}={ep.value}"""
+                  for ep in self.record.entry_points)
+
+        def locate_file(self, path):
+            raise RuntimeError("This distribution has no file system")
+
+This basic implementation should provide metadata and entry points for
+packages served by the ``DatabaseImporter``, assuming that the
+``record`` supplies suitable ``.name``, ``.version``, and
+``.entry_points`` attributes.
+
+The ``DatabaseDistribution`` may also provide other metadata files, like
+``RECORD`` (required for ``Distribution.files``) or override the
+implementation of ``Distribution.files``. See the source for more inspiration.
+
 
 .. _`entry point API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
 .. _`metadata API`: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#metadata-api