From 23f9dfc655f2492e2d6f2dd00db92eac70e5b804 Mon Sep 17 00:00:00 2001 From: Adrien Ferrand Date: Tue, 12 Sep 2023 17:18:57 +0200 Subject: [PATCH] Migrate `pkg_resources` usages to `importlib.metadata` (#9749) * Migrate entrypoint logic from pkg_resources to importlib.metadata * Usage of importlib_metadata up to Python 3.9 to align API behavior to Python 3.10 --------- Co-authored-by: Adrien Ferrand Co-authored-by: Adrien Ferrand --- certbot/certbot/_internal/plugins/disco.py | 44 +++++----- .../_internal/tests/plugins/disco_test.py | 80 ++++++++++++------- certbot/setup.py | 1 + tools/oldest_constraints.txt | 15 ++-- tools/pinning/oldest/pyproject.toml | 2 +- tools/requirements.txt | 29 ++++--- 6 files changed, 99 insertions(+), 72 deletions(-) diff --git a/certbot/certbot/_internal/plugins/disco.py b/certbot/certbot/_internal/plugins/disco.py index 2ee07f083..c0426bcd8 100644 --- a/certbot/certbot/_internal/plugins/disco.py +++ b/certbot/certbot/_internal/plugins/disco.py @@ -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): diff --git a/certbot/certbot/_internal/tests/plugins/disco_test.py b/certbot/certbot/_internal/tests/plugins/disco_test.py index 97b3f2176..9b4903f8b 100644 --- a/certbot/certbot/_internal/tests/plugins/disco_test.py +++ b/certbot/certbot/_internal/tests/plugins/disco_test.py @@ -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() diff --git a/certbot/setup.py b/certbot/setup.py index 2aeebe9a3..f607d558b 100644 --- a/certbot/setup.py +++ b/certbot/setup.py @@ -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', diff --git a/tools/oldest_constraints.txt b/tools/oldest_constraints.txt index 59f6e0d41..283a0ab57 100644 --- a/tools/oldest_constraints.txt +++ b/tools/oldest_constraints.txt @@ -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" boto3==1.15.15 ; python_version >= "3.7" and python_version < "3.8" @@ -30,8 +31,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" @@ -48,7 +49,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" @@ -61,7 +62,7 @@ pyparsing==2.2.1 ; python_version >= "3.7" and python_version < "3.8" pyrfc3339==1.0 ; python_version >= "3.7" and python_version < "3.8" pytest-cov==4.1.0 ; python_version >= "3.7" and python_version < "3.8" pytest-xdist==3.3.1 ; python_version >= "3.7" and python_version < "3.8" -pytest==7.4.0 ; python_version >= "3.7" and python_version < "3.8" +pytest==7.4.2 ; python_version >= "3.7" and python_version < "3.8" python-augeas==0.5.0 ; python_version >= "3.7" and python_version < "3.8" python-dateutil==2.8.2 ; python_version >= "3.7" and python_version < "3.8" python-digitalocean==1.11 ; python_version >= "3.7" and python_version < "3.8" @@ -74,7 +75,7 @@ rsa==4.9 ; python_version >= "3.7" and python_version < "3.8" s3transfer==0.3.7 ; python_version >= "3.7" and python_version < "3.8" 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" -tldextract==3.4.4 ; 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" 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" @@ -87,13 +88,13 @@ types-python-dateutil==2.8.19.14 ; python_version >= "3.7" and python_version < types-pytz==2023.3.0.1 ; python_version >= "3.7" and python_version < "3.8" types-pywin32==306.0.0.4 ; python_version >= "3.7" and python_version < "3.8" types-requests==2.31.0.2 ; python_version >= "3.7" and python_version < "3.8" -types-setuptools==68.1.0.0 ; 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" 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.3 ; 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" diff --git a/tools/pinning/oldest/pyproject.toml b/tools/pinning/oldest/pyproject.toml index 256e84ffb..28e0ccabe 100644 --- a/tools/pinning/oldest/pyproject.toml +++ b/tools/pinning/oldest/pyproject.toml @@ -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" diff --git a/tools/requirements.txt b/tools/requirements.txt index c1588bedd..724975055 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -10,7 +10,7 @@ apacheconfig==0.3.2 ; python_version >= "3.7" and python_version < "4.0" appnope==0.1.3 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "darwin" astroid==2.13.5 ; python_full_version >= "3.7.2" and python_version < "4.0" attrs==23.1.0 ; python_version >= "3.7" and python_version < "4.0" -azure-core==1.29.3 ; python_version >= "3.7" and python_version < "4.0" +azure-core==1.29.4 ; python_version >= "3.7" and python_version < "4.0" azure-devops==7.1.0b3 ; python_version >= "3.7" and python_version < "4.0" babel==2.12.1 ; python_version >= "3.7" and python_version < "4.0" backcall==0.2.0 ; python_version >= "3.7" and python_version < "4.0" @@ -18,8 +18,8 @@ backports-cached-property==1.0.2 ; python_version >= "3.7" and python_version < bcrypt==4.0.1 ; python_version >= "3.7" and python_version < "4.0" beautifulsoup4==4.12.2 ; python_version >= "3.7" and python_version < "4.0" bleach==6.0.0 ; python_version >= "3.7" and python_version < "4.0" -boto3==1.28.36 ; python_version >= "3.7" and python_version < "4.0" -botocore==1.31.36 ; python_version >= "3.7" and python_version < "4.0" +boto3==1.28.43 ; python_version >= "3.7" and python_version < "4.0" +botocore==1.31.43 ; python_version >= "3.7" and python_version < "4.0" cachecontrol==0.12.14 ; python_version >= "3.7" and python_version < "4.0" cachetools==5.3.1 ; python_version >= "3.7" and python_version < "4.0" cachy==0.3.0 ; 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.13.0 ; python_version >= "3.7" and python_version < "4.0" dnspython==2.3.0 ; python_version >= "3.7" and python_version < "4.0" -docutils==0.18.1 ; python_version >= "3.7" and python_version < "4.0" +docutils==0.19 ; 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.1 ; 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.97.0 ; 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-auth-httplib2==0.1.0 ; python_version >= "3.7" and python_version < "4.0" google-auth==2.22.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" @@ -58,7 +58,7 @@ httplib2==0.22.0 ; python_version >= "3.7" and python_version < "4.0" idna==3.4 ; python_version >= "3.7" and python_version < "4.0" imagesize==1.4.1 ; python_version >= "3.7" and python_version < "4.0" importlib-metadata==4.13.0 ; python_version >= "3.7" and python_version < "4.0" -importlib-resources==5.12.0 ; python_version >= "3.7" and python_version < "3.9" +importlib-resources==5.12.0 ; python_version >= "3.7" and python_version < "4.0" iniconfig==2.0.0 ; python_version >= "3.7" and python_version < "4.0" invoke==2.2.0 ; python_version >= "3.7" and python_version < "4.0" ipdb==0.13.13 ; python_version >= "3.7" and python_version < "4.0" @@ -104,7 +104,7 @@ poetry-core==1.3.2 ; python_version >= "3.7" and python_version < "4.0" poetry-plugin-export==1.2.0 ; python_version >= "3.7" and python_version < "4.0" poetry==1.2.2 ; python_version >= "3.7" and python_version < "4.0" prompt-toolkit==3.0.39 ; python_version >= "3.7" and python_version < "4.0" -protobuf==4.24.2 ; python_version >= "3.7" and python_version < "4.0" +protobuf==4.24.3 ; python_version >= "3.7" and python_version < "4.0" ptyprocess==0.7.0 ; python_version >= "3.7" and python_version < "4.0" py==1.11.0 ; python_version >= "3.7" and python_version < "4.0" pyasn1-modules==0.3.0 ; python_version >= "3.7" and python_version < "4.0" @@ -121,11 +121,11 @@ pyrfc3339==1.1 ; python_version >= "3.7" and python_version < "4.0" pyrsistent==0.19.3 ; python_version >= "3.7" and python_version < "4.0" pytest-cov==4.1.0 ; python_version >= "3.7" and python_version < "4.0" pytest-xdist==3.3.1 ; python_version >= "3.7" and python_version < "4.0" -pytest==7.4.0 ; python_version >= "3.7" and python_version < "4.0" +pytest==7.4.2 ; python_version >= "3.7" and python_version < "4.0" python-augeas==1.1.0 ; python_version >= "3.7" and python_version < "4.0" python-dateutil==2.8.2 ; python_version >= "3.7" and python_version < "4.0" python-digitalocean==1.17.0 ; python_version >= "3.7" and python_version < "4.0" -pytz==2023.3 ; python_version >= "3.7" and python_version < "4.0" +pytz==2023.3.post1 ; python_version >= "3.7" and python_version < "4.0" pywin32-ctypes==0.2.2 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32" pywin32==306 ; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32" pyyaml==6.0.1 ; python_version >= "3.7" and python_version < "4.0" @@ -147,17 +147,16 @@ 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==1.3.0 ; 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==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.4.4 ; python_version >= "3.7" and python_version < "4.0" -tomli==2.0.1 ; python_version >= "3.7" and python_version < "3.11" +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" 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" @@ -170,7 +169,7 @@ types-python-dateutil==2.8.19.14 ; python_version >= "3.7" and python_version < types-pytz==2023.3.0.1 ; python_version >= "3.7" and python_version < "4.0" types-pywin32==306.0.0.4 ; python_version >= "3.7" and python_version < "4.0" types-requests==2.31.0.2 ; python_version >= "3.7" and python_version < "4.0" -types-setuptools==68.1.0.0 ; python_version >= "3.7" and python_version < "4.0" +types-setuptools==68.2.0.0 ; python_version >= "3.7" and python_version < "4.0" types-six==1.16.21.9 ; python_version >= "3.7" and python_version < "4.0" types-urllib3==1.26.25.14 ; python_version >= "3.7" and python_version < "4.0" typing-extensions==4.7.1 ; python_version >= "3.7" and python_version < "4.0"