Repository: certbot/certbot

Issue:
Stop supporting `dist_name:plugin_name` for 3rd party plugins
>When Certbot was first released, 3rd party plugins names had to have the format `dist_name:plugin_name` instead of just `plugin_name` both on the CLI and in configuration files. Certbot has accepted the shorter `plugin_name` version for a while now and support for the format `dist_name:plugin_name` will be dropped.

I think the code here is responsible for loading third-party a second time with `with_prefix` set, and that's what we are going to be getting rid of:

https://github.com/certbot/certbot/blob/5c111d0bd1206d864d7cb93754e101f6073bc669/certbot/certbot/_internal/plugins/disco.py#L245-L253


Discussion:

Resolving patch:
diff --git a/certbot/certbot/_internal/plugins/disco.py b/certbot/certbot/_internal/plugins/disco.py
--- a/certbot/certbot/_internal/plugins/disco.py
+++ b/certbot/certbot/_internal/plugins/disco.py
@@ -55,32 +55,23 @@ class PluginEntryPoint:
     # this object is mutable, don't allow it to be hashed!
     __hash__ = None  # type: ignore
 
-    def __init__(self, entry_point: pkg_resources.EntryPoint, with_prefix: bool = False) -> None:
-        self.name = self.entry_point_to_plugin_name(entry_point, with_prefix)
+    def __init__(self, entry_point: pkg_resources.EntryPoint) -> None:
+        self.name = self.entry_point_to_plugin_name(entry_point)
         self.plugin_cls: Type[interfaces.Plugin] = entry_point.load()
         self.entry_point = entry_point
         self.warning_message: Optional[str] = None
         self._initialized: Optional[interfaces.Plugin] = None
         self._prepared: Optional[Union[bool, Error]] = None
-        self._hidden = False
-        self._long_description: Optional[str] = None
 
     def check_name(self, name: Optional[str]) -> bool:
         """Check if the name refers to this plugin."""
         if name == self.name:
-            if self.warning_message:
-                logger.warning(self.warning_message)
             return True
         return False
 
     @classmethod
-    def entry_point_to_plugin_name(cls, entry_point: pkg_resources.EntryPoint,
-                                   with_prefix: bool) -> str:
+    def entry_point_to_plugin_name(cls, entry_point: pkg_resources.EntryPoint) -> str:
         """Unique plugin name for an ``entry_point``"""
-        if with_prefix:
-            if not entry_point.dist:
-                raise errors.Error(f"Entrypoint {entry_point.name} has no distribution!")
-            return entry_point.dist.key + ":" + entry_point.name
         return entry_point.name
 
     @property
@@ -96,22 +87,12 @@ def description_with_name(self) -> str:
     @property
     def long_description(self) -> str:
         """Long description of the plugin."""
-        if self._long_description:
-            return self._long_description
         return getattr(self.plugin_cls, "long_description", self.description)
 
-    @long_description.setter
-    def long_description(self, description: str) -> None:
-        self._long_description = description
-
     @property
     def hidden(self) -> bool:
         """Should this plugin be hidden from UI?"""
-        return self._hidden or getattr(self.plugin_cls, "hidden", False)
-
-    @hidden.setter
-    def hidden(self, hide: bool) -> None:
-        self._hidden = hide
+        return getattr(self.plugin_cls, "hidden", False)
 
     def ifaces(self, *ifaces_groups: Iterable[Type]) -> bool:
         """Does plugin implements specified interface groups?"""
@@ -238,27 +219,14 @@ def find_all(cls) -> 'PluginsRegistry':
             pkg_resources.iter_entry_points(
                 constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT),)
         for entry_point in entry_points:
-            plugin_ep = cls._load_entry_point(entry_point, plugins, with_prefix=False)
-            # entry_point.dist cannot be None here, we would have blown up
-            # earlier, however, this assertion is needed for mypy.
-            assert entry_point.dist is not None
-            if entry_point.dist.key not in PREFIX_FREE_DISTRIBUTIONS:
-                prefixed_plugin_ep = cls._load_entry_point(entry_point, plugins, with_prefix=True)
-                prefixed_plugin_ep.hidden = True
-                message = (
-                    "Plugin legacy name {0} may be removed in a future version. "
-                    "Please use {1} instead.").format(prefixed_plugin_ep.name, plugin_ep.name)
-                prefixed_plugin_ep.warning_message = message
-                prefixed_plugin_ep.long_description = "(WARNING: {0}) {1}".format(
-                    message, prefixed_plugin_ep.long_description)
+            cls._load_entry_point(entry_point, plugins)
 
         return cls(plugins)
 
     @classmethod
     def _load_entry_point(cls, entry_point: pkg_resources.EntryPoint,
-                          plugins: Dict[str, PluginEntryPoint],
-                          with_prefix: bool) -> PluginEntryPoint:
-        plugin_ep = PluginEntryPoint(entry_point, with_prefix)
+                          plugins: Dict[str, PluginEntryPoint]) -> None:
+        plugin_ep = PluginEntryPoint(entry_point)
         if plugin_ep.name in plugins:
             other_ep = plugins[plugin_ep.name]
             plugin1 = plugin_ep.entry_point.dist.key if plugin_ep.entry_point.dist else "unknown"
@@ -271,8 +239,6 @@ def _load_entry_point(cls, entry_point: pkg_resources.EntryPoint,
             logger.warning(
                 "%r does not inherit from Plugin, skipping", plugin_ep)
 
-        return plugin_ep
-
     def __getitem__(self, name: str) -> PluginEntryPoint:
         return self._plugins[name]
 


Test patch:
diff --git a/certbot/tests/plugins/disco_test.py b/certbot/tests/plugins/disco_test.py
--- a/certbot/tests/plugins/disco_test.py
+++ b/certbot/tests/plugins/disco_test.py
@@ -60,21 +60,7 @@ def test_entry_point_to_plugin_name_not_prefixed(self):
 
         for entry_point, name in names.items():
             self.assertEqual(
-                name, PluginEntryPoint.entry_point_to_plugin_name(entry_point, with_prefix=False))
-
-    def test_entry_point_to_plugin_name_prefixed(self):
-        from certbot._internal.plugins.disco import PluginEntryPoint
-
-        names = {
-            self.ep1: "p1:ep1",
-            self.ep1prim: "p2:ep1",
-            self.ep2: "p2:ep2",
-            self.ep3: "p3:ep3",
-        }
-
-        for entry_point, name in names.items():
-            self.assertEqual(
-                name, PluginEntryPoint.entry_point_to_plugin_name(entry_point, with_prefix=True))
+                name, PluginEntryPoint.entry_point_to_plugin_name(entry_point))
 
     def test_description(self):
         self.assertIn("temporary webserver", self.plugin_ep.description)
@@ -232,8 +218,7 @@ def test_find_all(self):
         self.assertIs(plugins["wr"].entry_point, EP_WR)
         self.assertIs(plugins["ep1"].plugin_cls, null.Installer)
         self.assertIs(plugins["ep1"].entry_point, self.ep1)
-        self.assertIs(plugins["p1:ep1"].plugin_cls, null.Installer)
-        self.assertIs(plugins["p1:ep1"].entry_point, self.ep1)
+        self.assertNotIn("p1:ep1", plugins)
 
     def test_getitem(self):
         self.assertEqual(self.plugin_ep, self.reg["mock"])