Merge branch 'master' into use-new-lexicon-api

This commit is contained in:
Adrien Ferrand 2023-09-11 22:50:27 +02:00
commit 38f82cf12d
6 changed files with 95 additions and 66 deletions

View file

@ -1,5 +1,4 @@
"""Utilities for plugins discovery and selection."""
import itertools
import logging
import sys
from typing import Callable
@ -13,8 +12,6 @@ from typing import Optional
from typing import Type
from typing import Union
import pkg_resources
from certbot import configuration
from certbot import errors
from certbot import interfaces
@ -22,6 +19,11 @@ from certbot._internal import constants
from certbot.compat import os
from certbot.errors import Error
if sys.version_info >= (3, 10): # pragma: no cover
import importlib.metadata as importlib_metadata
else:
import importlib_metadata
logger = logging.getLogger(__name__)
@ -35,7 +37,7 @@ 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) -> None:
def __init__(self, entry_point: importlib_metadata.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
@ -50,7 +52,7 @@ class PluginEntryPoint:
return False
@classmethod
def entry_point_to_plugin_name(cls, entry_point: pkg_resources.EntryPoint) -> str:
def entry_point_to_plugin_name(cls, entry_point: importlib_metadata.EntryPoint) -> str:
"""Unique plugin name for an ``entry_point``"""
return entry_point.name
@ -75,7 +77,7 @@ class PluginEntryPoint:
return getattr(self.plugin_cls, "hidden", False)
def ifaces(self, *ifaces_groups: Iterable[Type]) -> bool:
"""Does plugin implements specified interface groups?"""
"""Does plugin implement specified interface groups?"""
return not ifaces_groups or any(
all(issubclass(self.plugin_cls, iface)
for iface in ifaces)
@ -89,7 +91,6 @@ class PluginEntryPoint:
def init(self, config: Optional[configuration.NamespaceConfig] = None) -> interfaces.Plugin:
"""Memoized plugin initialization."""
if not self._initialized:
self.entry_point.require() # fetch extras!
# For plugins implementing ABCs Plugin, Authenticator or Installer, the following
# line will raise an exception if some implementations of abstract methods are missing.
self._initialized = self.plugin_cls(config, self.name)
@ -181,32 +182,31 @@ class PluginsRegistry(Mapping):
plugin_paths = plugin_paths_string.split(':') if plugin_paths_string else []
# XXX should ensure this only happens once
sys.path.extend(plugin_paths)
for plugin_path in plugin_paths:
pkg_resources.working_set.add_entry(plugin_path)
entry_points = itertools.chain(
pkg_resources.iter_entry_points(
constants.SETUPTOOLS_PLUGINS_ENTRY_POINT),
pkg_resources.iter_entry_points(
constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT),)
for entry_point in entry_points:
entry_points = list(importlib_metadata.entry_points(
group=constants.SETUPTOOLS_PLUGINS_ENTRY_POINT))
old_entry_points = list(importlib_metadata.entry_points(
group=constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT))
for entry_point in entry_points + old_entry_points:
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
f"The '{entry_point.module}' 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
def _load_entry_point(cls, entry_point: pkg_resources.EntryPoint,
def _load_entry_point(cls, entry_point: importlib_metadata.EntryPoint,
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"
plugin2 = other_ep.entry_point.dist.key if other_ep.entry_point.dist else "unknown"
plugin1_dist = plugin_ep.entry_point.dist
plugin2_dist = other_ep.entry_point.dist
plugin1 = plugin1_dist.name.lower() if plugin1_dist else "unknown"
plugin2 = plugin2_dist.name.lower() if plugin2_dist else "unknown"
raise Exception("Duplicate plugin name {0} from {1} and {2}.".format(
plugin_ep.name, plugin1, plugin2))
if issubclass(plugin_ep.plugin_cls, interfaces.Plugin):

View file

@ -6,7 +6,6 @@ from typing import List
import unittest
from unittest import mock
import pkg_resources
import pytest
from certbot import errors
@ -15,30 +14,55 @@ from certbot._internal.plugins import null
from certbot._internal.plugins import standalone
from certbot._internal.plugins import webroot
EP_SA = pkg_resources.EntryPoint(
"sa", "certbot._internal.plugins.standalone",
attrs=("Authenticator",),
dist=mock.MagicMock(key="certbot"))
EP_WR = pkg_resources.EntryPoint(
"wr", "certbot._internal.plugins.webroot",
attrs=("Authenticator",),
dist=mock.MagicMock(key="certbot"))
if sys.version_info >= (3, 10): # pragma: no cover
import importlib.metadata as importlib_metadata
else:
import importlib_metadata
class _EntryPointLoadFail(importlib_metadata.EntryPoint):
def load(self):
raise RuntimeError("Loading failure")
EP_SA = importlib_metadata.EntryPoint(
name="sa",
value="certbot._internal.plugins.standalone:Authenticator",
group="certbot.plugins")
EP_WR = importlib_metadata.EntryPoint(
name="wr",
value="certbot._internal.plugins.webroot:Authenticator",
group="certbot.plugins")
EP_SA_LOADFAIL = _EntryPointLoadFail(
name="sa",
value="certbot._internal.plugins.standalone:Authenticator",
group="certbot.plugins")
class PluginEntryPointTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.disco.PluginEntryPoint."""
def setUp(self):
self.ep1 = pkg_resources.EntryPoint(
"ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
self.ep1prim = pkg_resources.EntryPoint(
"ep1", "p2.ep2", dist=mock.MagicMock(key="p2"))
self.ep1 = importlib_metadata.EntryPoint(
name="ep1",
value="p1.ep1:Authenticator",
group="certbot.plugins")
self.ep1prim = importlib_metadata.EntryPoint(
name="ep1",
value="p2.pe2:Authenticator",
group="certbot.plugins")
# nested
self.ep2 = pkg_resources.EntryPoint(
"ep2", "p2.foo.ep2", dist=mock.MagicMock(key="p2"))
self.ep2 = importlib_metadata.EntryPoint(
name="ep2",
value="p2.foo.ep2:Authenticator",
group="certbot.plugins")
# project name != top-level package name
self.ep3 = pkg_resources.EntryPoint(
"ep3", "a.ep3", dist=mock.MagicMock(key="p3"))
self.ep3 = importlib_metadata.EntryPoint(
name="ep3",
value="a.ep3:Authenticator",
group="certbot.plugins")
from certbot._internal.plugins.disco import PluginEntryPoint
self.plugin_ep = PluginEntryPoint(EP_SA)
@ -172,16 +196,18 @@ class PluginsRegistryTest(unittest.TestCase):
self.plugin_ep.__hash__.side_effect = TypeError
self.plugins = {self.plugin_ep.name: self.plugin_ep}
self.reg = self._create_new_registry(self.plugins)
self.ep1 = pkg_resources.EntryPoint(
"ep1", "p1.ep1", dist=mock.MagicMock(key="p1"))
self.ep1 = importlib_metadata.EntryPoint(
name="ep1",
value="p1.ep1",
group="certbot.plugins")
def test_find_all(self):
from certbot._internal.plugins.disco import PluginsRegistry
with mock.patch("certbot._internal.plugins.disco.pkg_resources") as mock_pkg:
mock_pkg.iter_entry_points.side_effect = [
iter([EP_SA]), iter([EP_WR, self.ep1])
with mock.patch("certbot._internal.plugins.disco.importlib_metadata") as mock_meta:
mock_meta.entry_points.side_effect = [
[EP_SA], [EP_WR, self.ep1],
]
with mock.patch.object(pkg_resources.EntryPoint, 'load') as mock_load:
with mock.patch.object(importlib_metadata.EntryPoint, 'load') as mock_load:
mock_load.side_effect = [
standalone.Authenticator, webroot.Authenticator,
null.Installer, null.Installer]
@ -196,10 +222,10 @@ class PluginsRegistryTest(unittest.TestCase):
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 mock.patch("certbot._internal.plugins.disco.importlib_metadata") as mock_meta:
#EP_SA.load = None # This triggers a TypeError when the entrypoint loads
mock_meta.entry_points.side_effect = [
[EP_SA_LOADFAIL], [EP_WR, self.ep1],
]
with self.assertRaises(errors.PluginError) as cm:
PluginsRegistry.find_all()

View file

@ -36,6 +36,7 @@ install_requires = [
'cryptography>=3.2.1',
'distro>=1.0.1',
'importlib_resources>=1.3.1; python_version < "3.9"',
'importlib_metadata>=4.6; python_version < "3.10"',
'josepy>=1.13.0',
'parsedatetime>=2.4',
'pyrfc3339',

View file

@ -1,6 +1,7 @@
# This file was generated by tools/pinning/oldest/repin.sh and can be updated using
# that script.
apacheconfig==0.3.2 ; python_version >= "3.7" and python_version < "3.8"
appdirs==1.4.4 ; python_version >= "3.7" and python_version < "3.8"
asn1crypto==0.24.0 ; python_version >= "3.7" and python_version < "3.8"
astroid==2.15.6 ; python_full_version >= "3.7.2" and python_version < "3.8"
beautifulsoup4==4.12.2 ; python_version >= "3.7" and python_version < "3.8"
@ -11,7 +12,7 @@ certifi==2023.7.22 ; python_version >= "3.7" and python_version < "3.8"
cffi==1.11.5 ; python_version >= "3.7" and python_version < "3.8"
chardet==3.0.4 ; python_version >= "3.7" and python_version < "3.8"
cloudflare==1.5.1 ; python_version >= "3.7" and python_version < "3.8"
colorama==0.4.6 ; python_version >= "3.7" and python_version < "3.8" and sys_platform == "win32"
colorama==0.4.6 ; python_version < "3.8" and sys_platform == "win32" and python_version >= "3.7"
configargparse==1.5.3 ; python_version >= "3.7" and python_version < "3.8"
configobj==5.0.6 ; python_version >= "3.7" and python_version < "3.8"
coverage==7.2.7 ; python_version >= "3.7" and python_version < "3.8"
@ -31,8 +32,8 @@ google-api-python-client==1.6.5 ; python_version >= "3.7" and python_version < "
google-auth==2.16.0 ; python_version >= "3.7" and python_version < "3.8"
httplib2==0.9.2 ; python_version >= "3.7" and python_version < "3.8"
idna==2.6 ; python_version >= "3.7" and python_version < "3.8"
importlib-metadata==6.7.0 ; python_version >= "3.7" and python_version < "3.8"
importlib-resources==1.3.1 ; python_version >= "3.7" and python_version < "3.8"
importlib-metadata==4.6.4 ; python_version >= "3.7" and python_version < "3.8"
importlib-resources==5.12.0 ; python_version >= "3.7" and python_version < "3.8"
iniconfig==2.0.0 ; python_version >= "3.7" and python_version < "3.8"
ipaddress==1.0.16 ; python_version >= "3.7" and python_version < "3.8"
isort==5.11.5 ; python_full_version >= "3.7.2" and python_version < "3.8"
@ -49,7 +50,7 @@ packaging==23.1 ; python_version >= "3.7" and python_version < "3.8"
parsedatetime==2.4 ; python_version >= "3.7" and python_version < "3.8"
pbr==1.8.0 ; python_version >= "3.7" and python_version < "3.8"
pip==23.2.1 ; python_version >= "3.7" and python_version < "3.8"
platformdirs==3.10.0 ; python_version >= "3.7" and python_version < "3.8"
platformdirs==3.10.0 ; python_full_version >= "3.7.2" and python_version < "3.8"
pluggy==1.2.0 ; python_version >= "3.7" and python_version < "3.8"
ply==3.4 ; python_version >= "3.7" and python_version < "3.8"
py==1.11.0 ; python_version >= "3.7" and python_version < "3.8"
@ -77,10 +78,10 @@ setuptools==41.6.0 ; python_version >= "3.7" and python_version < "3.8"
six==1.11.0 ; python_version >= "3.7" and python_version < "3.8"
soupsieve==2.4.1 ; python_version >= "3.7" and python_version < "3.8"
tldextract==3.5.0 ; python_version >= "3.7" and python_version < "3.8"
tomli==2.0.1 ; python_version >= "3.7" and python_version < "3.8"
tomli==2.0.1 ; python_version < "3.8" and python_version >= "3.7"
tomlkit==0.12.1 ; python_full_version >= "3.7.2" and python_version < "3.8"
tox==1.9.2 ; python_version >= "3.7" and python_version < "3.8"
typed-ast==1.5.5 ; python_version >= "3.7" and python_version < "3.8"
typed-ast==1.5.5 ; python_version < "3.8" and python_version >= "3.7"
types-cryptography==3.3.23.2 ; python_version >= "3.7" and python_version < "3.8"
types-httplib2==0.22.0.2 ; python_version >= "3.7" and python_version < "3.8"
types-pyopenssl==23.0.0.0 ; python_version >= "3.7" and python_version < "3.8"
@ -92,10 +93,10 @@ types-requests==2.31.0.2 ; python_version >= "3.7" and python_version < "3.8"
types-setuptools==68.2.0.0 ; python_version >= "3.7" and python_version < "3.8"
types-six==1.16.21.9 ; python_version >= "3.7" and python_version < "3.8"
types-urllib3==1.26.25.14 ; python_version >= "3.7" and python_version < "3.8"
typing-extensions==4.7.1 ; python_version >= "3.7" and python_version < "3.8"
typing-extensions==4.7.1 ; python_version < "3.8" and python_version >= "3.7"
uritemplate==3.0.1 ; python_version >= "3.7" and python_version < "3.8"
urllib3==1.24.2 ; python_version >= "3.7" and python_version < "3.8"
virtualenv==20.24.5 ; python_version >= "3.7" and python_version < "3.8"
virtualenv==20.4.7 ; python_version >= "3.7" and python_version < "3.8"
wheel==0.33.6 ; python_version >= "3.7" and python_version < "3.8"
wrapt==1.15.0 ; python_full_version >= "3.7.2" and python_version < "3.8"
zipp==3.15.0 ; python_version >= "3.7" and python_version < "3.8"

View file

@ -61,7 +61,7 @@ google-api-python-client = "1.6.5"
google-auth = "2.16.0"
httplib2 = "0.9.2"
idna = "2.6"
importlib-resources = "1.3.1"
importlib-metadata = "4.6.4"
ipaddress = "1.0.16"
ndg-httpsclient = "0.3.2"
parsedatetime = "2.4"

View file

@ -28,7 +28,7 @@ cffi==1.15.1 ; python_version >= "3.7" and python_version < "4.0"
charset-normalizer==3.2.0 ; python_version >= "3.7" and python_version < "4.0"
cleo==1.0.0a5 ; python_version >= "3.7" and python_version < "4.0"
cloudflare==2.11.7 ; python_version >= "3.7" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.7" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
colorama==0.4.6 ; python_version < "4.0" and sys_platform == "win32" and python_version >= "3.7" or python_version >= "3.7" and python_version < "4.0" and platform_system == "Windows"
configargparse==1.7 ; python_version >= "3.7" and python_version < "4.0"
configobj==5.0.8 ; python_version >= "3.7" and python_version < "4.0"
coverage==7.2.7 ; python_version >= "3.7" and python_version < "4.0"
@ -42,14 +42,14 @@ distlib==0.3.7 ; python_version >= "3.7" and python_version < "4.0"
distro==1.8.0 ; python_version >= "3.7" and python_version < "4.0"
dns-lexicon==3.14.1 ; python_version >= "3.7" and python_version < "4.0"
dnspython==2.3.0 ; python_version >= "3.7" and python_version < "4.0"
docutils==0.19 ; python_version >= "3.7" and python_version < "4.0"
docutils==0.18.1 ; python_version >= "3.7" and python_version < "4.0"
dulwich==0.20.50 ; python_version >= "3.7" and python_version < "4.0"
exceptiongroup==1.1.3 ; python_version >= "3.7" and python_version < "3.11"
execnet==2.0.2 ; python_version >= "3.7" and python_version < "4.0"
fabric==3.2.2 ; python_version >= "3.7" and python_version < "4.0"
filelock==3.12.2 ; python_version >= "3.7" and python_version < "4.0"
google-api-core==2.11.1 ; python_version >= "3.7" and python_version < "4.0"
google-api-python-client==2.98.0 ; python_version >= "3.7" and python_version < "4.0"
google-api-python-client==2.99.0 ; python_version >= "3.7" and python_version < "4.0"
google-auth-httplib2==0.1.1 ; python_version >= "3.7" and python_version < "4.0"
google-auth==2.23.0 ; python_version >= "3.7" and python_version < "4.0"
googleapis-common-protos==1.60.0 ; python_version >= "3.7" and python_version < "4.0"
@ -97,7 +97,7 @@ pickleshare==0.7.5 ; python_version >= "3.7" and python_version < "4.0"
pip==23.2.1 ; python_version >= "3.7" and python_version < "4.0"
pkginfo==1.9.6 ; python_version >= "3.7" and python_version < "4.0"
pkgutil-resolve-name==1.3.10 ; python_version >= "3.7" and python_version < "3.9"
platformdirs==2.6.2 ; python_version >= "3.7" and python_version < "4.0"
platformdirs==2.6.2 ; python_version < "4.0" and python_version >= "3.7"
pluggy==1.2.0 ; python_version >= "3.7" and python_version < "4.0"
ply==3.11 ; python_version >= "3.7" and python_version < "4.0"
poetry-core==1.3.2 ; python_version >= "3.7" and python_version < "4.0"
@ -147,21 +147,22 @@ shellingham==1.5.3 ; python_version >= "3.7" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.7" and python_version < "4.0"
snowballstemmer==2.2.0 ; python_version >= "3.7" and python_version < "4.0"
soupsieve==2.4.1 ; python_version >= "3.7" and python_version < "4.0"
sphinx-rtd-theme==0.5.1 ; python_version >= "3.7" and python_version < "4.0"
sphinx-rtd-theme==1.3.0 ; python_version >= "3.7" and python_version < "4.0"
sphinx==5.3.0 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-applehelp==1.0.2 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-devhelp==1.0.2 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-htmlhelp==2.0.0 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-jquery==4.1 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-jsmath==1.0.1 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.7" and python_version < "4.0"
sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.7" and python_version < "4.0"
tldextract==3.5.0 ; python_version >= "3.7" and python_version < "4.0"
tomli==2.0.1 ; python_version >= "3.7" and python_full_version <= "3.11.0a6"
tomlkit==0.12.1 ; python_version >= "3.7" and python_version < "4.0"
tomlkit==0.12.1 ; python_version < "4.0" and python_version >= "3.7"
tox==3.28.0 ; python_version >= "3.7" and python_version < "4.0"
traitlets==5.9.0 ; python_version >= "3.7" and python_version < "4.0"
twine==4.0.2 ; python_version >= "3.7" and python_version < "4.0"
typed-ast==1.5.5 ; python_version >= "3.7" and python_version < "3.8"
typed-ast==1.5.5 ; python_version < "3.8" and python_version >= "3.7"
types-httplib2==0.22.0.2 ; python_version >= "3.7" and python_version < "4.0"
types-pyopenssl==23.2.0.2 ; python_version >= "3.7" and python_version < "4.0"
types-pyrfc3339==1.1.1.5 ; python_version >= "3.7" and python_version < "4.0"
@ -179,7 +180,7 @@ virtualenv==20.21.1 ; python_version >= "3.7" and python_version < "4.0"
wcwidth==0.2.6 ; python_version >= "3.7" and python_version < "4.0"
webencodings==0.5.1 ; python_version >= "3.7" and python_version < "4.0"
wheel==0.41.2 ; python_version >= "3.7" and python_version < "4.0"
wrapt==1.15.0 ; python_version >= "3.7" and python_version < "4.0"
wrapt==1.15.0 ; python_version < "4.0" and python_version >= "3.7"
xattr==0.9.9 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "darwin"
yarg==0.1.9 ; python_version >= "3.7" and python_version < "4.0"
zipp==3.15.0 ; python_version >= "3.7" and python_version < "4.0"