diff --git a/certbot/certbot/_internal/plugins/disco.py b/certbot/certbot/_internal/plugins/disco.py index 5e767ae6d..2ee07f083 100644 --- a/certbot/certbot/_internal/plugins/disco.py +++ b/certbot/certbot/_internal/plugins/disco.py @@ -189,8 +189,14 @@ class PluginsRegistry(Mapping): pkg_resources.iter_entry_points( constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT),) for entry_point in entry_points: - cls._load_entry_point(entry_point, plugins) - + try: + cls._load_entry_point(entry_point, plugins) + except Exception as e: + raise errors.PluginError( + f"The '{entry_point.module_name}' plugin errored while loading: {e}. " + "You may need to remove or update this plugin. The Certbot log will " + "contain the full error details and this should be reported to the " + "plugin developer.") from e return cls(plugins) @classmethod diff --git a/certbot/certbot/_internal/tests/plugins/disco_test.py b/certbot/certbot/_internal/tests/plugins/disco_test.py index 0a0fd9826..97b3f2176 100644 --- a/certbot/certbot/_internal/tests/plugins/disco_test.py +++ b/certbot/certbot/_internal/tests/plugins/disco_test.py @@ -194,6 +194,17 @@ class PluginsRegistryTest(unittest.TestCase): assert plugins["ep1"].entry_point is self.ep1 assert "p1:ep1" not in plugins + def test_find_all_error_message(self): + from certbot._internal.plugins.disco import PluginsRegistry + with mock.patch("certbot._internal.plugins.disco.pkg_resources") as mock_pkg: + EP_SA.load = None # This triggers a TypeError when the entrypoint loads + mock_pkg.iter_entry_points.side_effect = [ + iter([EP_SA]), iter([EP_WR, self.ep1]) + ] + with self.assertRaises(errors.PluginError) as cm: + PluginsRegistry.find_all() + assert "standalone' plugin errored" in str(cm.exception) + def test_getitem(self): assert self.plugin_ep == self.reg["mock"]