1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-26 07:41:33 +03:00

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 <adrien.ferrand@amadeus.com>
Co-authored-by: Adrien Ferrand <adrien.ferrand@arteris.com>
This commit is contained in:
Adrien Ferrand
2023-09-12 17:18:57 +02:00
committed by GitHub
parent cc359dab46
commit 23f9dfc655
6 changed files with 99 additions and 72 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"
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"

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

@@ -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"