remove importlib_resources (#10076)

this is part of my work on
https://github.com/certbot/certbot/issues/10035 based on erica's comment
at
https://github.com/certbot/certbot/issues/10035#issuecomment-2452212686
This commit is contained in:
Brad Warren 2024-12-06 12:37:17 -08:00 committed by GitHub
parent 3f9387bd15
commit 7a48c235a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 66 additions and 146 deletions

View file

@ -3,6 +3,7 @@
.. warning:: This module is not part of the public API.
"""
import importlib.resources
import os
import sys
@ -12,16 +13,11 @@ import josepy as jose
from josepy.util import ComparableECKey
from OpenSSL import crypto
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
def load_vector(*names):
"""Load contents of a test vector."""
# luckily, resource_string opens file in binary mode
vector_ref = importlib_resources.files(__package__).joinpath('testdata', *names)
vector_ref = importlib.resources.files(__package__).joinpath('testdata', *names)
return vector_ref.read_bytes()

View file

@ -23,15 +23,6 @@ docs_extras = [
]
test_extras = [
# In theory we could scope importlib_resources to env marker 'python_version<"3.9"'. But this
# makes the pinning mechanism emit warnings when running `poetry lock` because in the corner
# case of an extra dependency with env marker coming from a setup.py file, it generate the
# invalid requirement 'importlib_resource>=1.3.1;python<=3.9;extra=="test"'.
# To fix the issue, we do not pass the env marker. This is fine because:
# - importlib_resources can be applied to any Python version,
# - this is a "test" extra dependency for limited audience,
# - it does not change anything at the end for the generated requirement files.
'importlib_resources>=1.3.1',
'pytest',
'pytest-xdist',
'typing-extensions',

View file

@ -2,10 +2,10 @@
import atexit
import binascii
import fnmatch
import importlib.resources
import logging
import re
import subprocess
import sys
from contextlib import ExitStack
from typing import Dict
from typing import Iterable
@ -17,12 +17,6 @@ from certbot import errors
from certbot import util
from certbot.compat import os
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
logger = logging.getLogger(__name__)
@ -257,6 +251,6 @@ def find_ssl_apache_conf(prefix: str) -> str:
"""
file_manager = ExitStack()
atexit.register(file_manager.close)
ref = (importlib_resources.files("certbot_apache").joinpath("_internal")
ref = (importlib.resources.files("certbot_apache").joinpath("_internal")
.joinpath("tls_configs").joinpath("{0}-options-ssl-apache.conf".format(prefix)))
return str(file_manager.enter_context(importlib_resources.as_file(ref)))
return str(file_manager.enter_context(importlib.resources.as_file(ref)))

View file

@ -1,14 +1,10 @@
"""Apache plugin constants."""
import atexit
import sys
import importlib.resources
from contextlib import ExitStack
from typing import Dict
from typing import List
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
"""Name of the mod_ssl config file as saved
@ -46,8 +42,8 @@ def _generate_augeas_lens_dir_static() -> str:
# Python process, and will be automatically cleaned up on exit.
file_manager = ExitStack()
atexit.register(file_manager.close)
augeas_lens_dir_ref = importlib_resources.files("certbot_apache") / "_internal" / "augeas_lens"
return str(file_manager.enter_context(importlib_resources.as_file(augeas_lens_dir_ref)))
augeas_lens_dir_ref = importlib.resources.files("certbot_apache") / "_internal" / "augeas_lens"
return str(file_manager.enter_context(importlib.resources.as_file(augeas_lens_dir_ref)))
AUGEAS_LENS_DIR = _generate_augeas_lens_dir_static()
"""Path to the Augeas lens directory"""

View file

@ -1653,15 +1653,12 @@ class InstallSslOptionsConfTest(util.ApacheTest):
file has been manually edited by the user, and will refuse to update it.
This test ensures that all necessary hashes are present.
"""
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
import importlib.resources
from certbot_apache._internal.constants import ALL_SSL_OPTIONS_HASHES
ref = importlib_resources.files("certbot_apache") / "_internal" / "tls_configs"
with importlib_resources.as_file(ref) as tls_configs_dir:
ref = importlib.resources.files("certbot_apache") / "_internal" / "tls_configs"
with importlib.resources.as_file(ref) as tls_configs_dir:
all_files = [os.path.join(tls_configs_dir, name) for name in os.listdir(tls_configs_dir)
if name.endswith('options-ssl-apache.conf')]
assert len(all_files) >= 1

View file

@ -9,7 +9,6 @@ install_requires = [
# https://github.com/certbot/certbot/issues/8761 for more info.
f'acme>={version}',
f'certbot>={version}',
'importlib_resources>=1.3.1; python_version < "3.9"',
'python-augeas',
]

View file

@ -2,15 +2,10 @@
"""General purpose nginx test configuration generator."""
import atexit
import getpass
import sys
import importlib.resources
from contextlib import ExitStack
from typing import Optional
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
def construct_nginx_config(nginx_root: str, nginx_webroot: str, http_port: int, https_port: int,
other_port: int, default_server: bool, key_path: Optional[str] = None,
@ -32,16 +27,16 @@ def construct_nginx_config(nginx_root: str, nginx_webroot: str, http_port: int,
if not key_path:
file_manager = ExitStack()
atexit.register(file_manager.close)
ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
ref = (importlib.resources.files('certbot_integration_tests').joinpath('assets')
.joinpath('key.pem'))
key_path = str(file_manager.enter_context(importlib_resources.as_file(ref)))
key_path = str(file_manager.enter_context(importlib.resources.as_file(ref)))
if not cert_path:
file_manager = ExitStack()
atexit.register(file_manager.close)
ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
ref = (importlib.resources.files('certbot_integration_tests').joinpath('assets')
.joinpath('cert.pem'))
cert_path = str(file_manager.enter_context(importlib_resources.as_file(ref)))
cert_path = str(file_manager.enter_context(importlib.resources.as_file(ref)))
return '''\
# This error log will be written regardless of server scope error_log

View file

@ -1,6 +1,6 @@
"""Module to handle the context of RFC2136 integration tests."""
from contextlib import contextmanager
import sys
import importlib.resources
import tempfile
from typing import Generator
from typing import Iterable
@ -11,11 +11,6 @@ import pytest
from certbot_integration_tests.certbot_tests import context as certbot_context
from certbot_integration_tests.utils import certbot_call
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
"""Integration test context for certbot-dns-rfc2136"""
@ -48,9 +43,9 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
:yields: Path to credentials file
:rtype: str
"""
src_ref_file = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
src_ref_file = (importlib.resources.files('certbot_integration_tests').joinpath('assets')
.joinpath('bind-config').joinpath(f'rfc2136-credentials-{label}.ini.tpl'))
with importlib_resources.as_file(src_ref_file) as src_file:
with importlib.resources.as_file(src_ref_file) as src_file:
with open(src_file, 'r') as f:
contents = f.read().format(
server_address=self._dns_xdist['address'],

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python
"""Module to setup an RFC2136-capable DNS server"""
import importlib.resources
import os
import os.path
import shutil
@ -17,11 +18,6 @@ from typing import Type
from certbot_integration_tests.utils import constants
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
BIND_DOCKER_IMAGE = "internetsystemsconsortium/bind9:9.20"
BIND_BIND_ADDRESS = ("127.0.0.1", 45953)
@ -83,8 +79,8 @@ class DNSServer:
def _configure_bind(self) -> None:
"""Configure the BIND9 server based on the prebaked configuration"""
ref = importlib_resources.files("certbot_integration_tests") / "assets" / "bind-config"
with importlib_resources.as_file(ref) as path:
ref = importlib.resources.files("certbot_integration_tests") / "assets" / "bind-config"
with importlib.resources.as_file(ref) as path:
for directory in ("conf", "zones"):
shutil.copytree(
os.path.join(path, directory), os.path.join(self.bind_root, directory)

View file

@ -7,6 +7,7 @@ import contextlib
import errno
import functools
import http.server as SimpleHTTPServer
import importlib.resources
import os
import re
import shutil
@ -36,11 +37,6 @@ import requests
from certbot_integration_tests.utils.constants import PEBBLE_ALTERNATE_ROOTS
from certbot_integration_tests.utils.constants import PEBBLE_MANAGEMENT_URL
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
RSA_KEY_TYPE = 'rsa'
ECDSA_KEY_TYPE = 'ecdsa'
@ -124,9 +120,9 @@ def generate_test_file_hooks(config_dir: str, hook_probe: str) -> None:
"""
file_manager = contextlib.ExitStack()
atexit.register(file_manager.close)
hook_path_ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
hook_path_ref = (importlib.resources.files('certbot_integration_tests').joinpath('assets')
.joinpath('hook.py'))
hook_path = str(file_manager.enter_context(importlib_resources.as_file(hook_path_ref)))
hook_path = str(file_manager.enter_context(importlib.resources.as_file(hook_path_ref)))
for hook_dir in list_renewal_hooks_dirs(config_dir):
# We want an equivalent of bash `chmod -p $HOOK_DIR, that does not fail if one folder of
@ -261,9 +257,9 @@ def load_sample_data_path(workspace: str) -> str:
:returns: the path to the loaded sample data directory
:rtype: str
"""
original_ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
original_ref = (importlib.resources.files('certbot_integration_tests').joinpath('assets')
.joinpath('sample-config'))
with importlib_resources.as_file(original_ref) as original:
with importlib.resources.as_file(original_ref) as original:
copied = os.path.join(workspace, 'sample-config')
shutil.copytree(original, copied, symlinks=True)

View file

@ -1,10 +1,10 @@
# pylint: disable=missing-module-docstring
import atexit
import importlib.resources
import io
import json
import os
import stat
import sys
import zipfile
from contextlib import ExitStack
from typing import Optional, Tuple
@ -14,11 +14,6 @@ import requests
from certbot_integration_tests.utils.constants import DEFAULT_HTTP_01_PORT
from certbot_integration_tests.utils.constants import MOCK_OCSP_SERVER_PORT
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
PEBBLE_VERSION = 'v2.5.1'
@ -26,8 +21,8 @@ def fetch(workspace: str, http_01_port: int = DEFAULT_HTTP_01_PORT) -> Tuple[str
# pylint: disable=missing-function-docstring
file_manager = ExitStack()
atexit.register(file_manager.close)
pebble_path_ref = importlib_resources.files('certbot_integration_tests') / 'assets'
assets_path = str(file_manager.enter_context(importlib_resources.as_file(pebble_path_ref)))
pebble_path_ref = importlib.resources.files('certbot_integration_tests') / 'assets'
assets_path = str(file_manager.enter_context(importlib.resources.as_file(pebble_path_ref)))
pebble_path = _fetch_asset('pebble', assets_path)
challtestsrv_path = _fetch_asset('pebble-challtestsrv', assets_path)

View file

@ -6,7 +6,6 @@ version = '0.32.0.dev0'
install_requires = [
'coverage',
'cryptography',
'importlib_resources>=1.3.1; python_version < "3.9"',
'pyopenssl',
'pytest',
'pytest-cov',

View file

@ -3,10 +3,10 @@
import atexit
from contextlib import ExitStack
import logging
import importlib.resources
import re
import socket
import subprocess
import sys
import tempfile
import time
from typing import Any
@ -40,11 +40,6 @@ from certbot_nginx._internal import nginxparser
from certbot_nginx._internal import obj
from certbot_nginx._internal import parser
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
NAME_RANK = 0
START_WILDCARD_RANK = 1
END_WILDCARD_RANK = 2
@ -171,10 +166,10 @@ class NginxConfigurator(common.Configurator):
file_manager = ExitStack()
atexit.register(file_manager.close)
ref = (importlib_resources.files("certbot_nginx").joinpath("_internal")
ref = (importlib.resources.files("certbot_nginx").joinpath("_internal")
.joinpath("tls_configs").joinpath(config_filename))
return str(file_manager.enter_context(importlib_resources.as_file(ref)))
return str(file_manager.enter_context(importlib.resources.as_file(ref)))
@property
def mod_ssl_conf(self) -> str:

View file

@ -1074,16 +1074,13 @@ class InstallSslOptionsConfTest(util.NginxTest):
file has been manually edited by the user, and will refuse to update it.
This test ensures that all necessary hashes are present.
"""
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
import importlib.resources
from certbot_nginx._internal.constants import ALL_SSL_OPTIONS_HASHES
tls_configs_ref = importlib_resources.files("certbot_nginx").joinpath(
tls_configs_ref = importlib.resources.files("certbot_nginx").joinpath(
"_internal", "tls_configs")
with importlib_resources.as_file(tls_configs_ref) as tls_configs_dir:
with importlib.resources.as_file(tls_configs_ref) as tls_configs_dir:
for tls_config_file in os.listdir(tls_configs_dir):
file_hash = crypto_util.sha256sum(os.path.join(tls_configs_dir, tls_config_file))
assert file_hash in ALL_SSL_OPTIONS_HASHES, \

View file

@ -1,5 +1,6 @@
"""Common utilities for certbot_nginx."""
import copy
import importlib.resources
import shutil
import tempfile
import sys
@ -15,11 +16,6 @@ from certbot.tests import util as test_util
from certbot_nginx._internal import configurator
from certbot_nginx._internal import nginxparser
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
class NginxTest(test_util.ConfigTestCase):
def setUp(self):
@ -86,8 +82,8 @@ class NginxTest(test_util.ConfigTestCase):
@contextmanager
def get_data_filename(filename):
"""Gets the filename of a test data file."""
ref = importlib_resources.files(__package__) / "testdata" / "etc_nginx"/ filename
with importlib_resources.as_file(ref) as path:
ref = importlib.resources.files(__package__) / "testdata" / "etc_nginx"/ filename
with importlib.resources.as_file(ref) as path:
yield path

View file

@ -9,7 +9,6 @@ install_requires = [
# https://github.com/certbot/certbot/issues/8761 for more info.
f'acme>={version}',
f'certbot>={version}',
'importlib_resources>=1.3.1; python_version < "3.9"',
# pyOpenSSL 23.1.0 is a bad release: https://github.com/pyca/pyopenssl/issues/1199
'PyOpenSSL>=17.5.0,!=23.1.0',
'pyparsing>=2.2.1',

View file

@ -15,6 +15,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
* Updated our Docker images to be based on Alpine Linux 3.20.
* Our runtime dependency on setuptools has been dropped from all Certbot
components.
* Certbot's packages no longer depend on library importlib_resources.
### Fixed

View file

@ -1,7 +1,7 @@
"""Certbot constants."""
import atexit
import importlib.resources
import logging
import sys
from contextlib import ExitStack
from typing import Any
from typing import Dict
@ -10,11 +10,6 @@ from acme import challenges
from certbot.compat import misc
from certbot.compat import os
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins"
"""Setuptools entry point group name for plugins."""
@ -228,8 +223,8 @@ def _generate_ssl_dhparams_src_static() -> str:
# Python process, and will be automatically cleaned up on exit.
file_manager = ExitStack()
atexit.register(file_manager.close)
ssl_dhparams_src_ref = importlib_resources.files("certbot") / "ssl-dhparams.pem"
return str(file_manager.enter_context(importlib_resources.as_file(ssl_dhparams_src_ref)))
ssl_dhparams_src_ref = importlib.resources.files("certbot") / "ssl-dhparams.pem"
return str(file_manager.enter_context(importlib.resources.as_file(ssl_dhparams_src_ref)))
SSL_DHPARAMS_SRC = _generate_ssl_dhparams_src_static()
"""Path to the nginx ssl_dhparams file found in the Certbot distribution."""

View file

@ -2,10 +2,10 @@
from abc import ABCMeta
from abc import abstractmethod
import argparse
import importlib.resources
import logging
import re
import shutil
import sys
import tempfile
from typing import Any
from typing import Callable
@ -31,11 +31,6 @@ from certbot.interfaces import Installer as AbstractInstaller
from certbot.interfaces import Plugin as AbstractPlugin
from certbot.plugins.storage import PluginStorage
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
logger = logging.getLogger(__name__)
@ -465,8 +460,8 @@ def dir_setup(test_dir: str, pkg: str) -> Tuple[str, str, str]: # pragma: no co
filesystem.chmod(config_dir, constants.CONFIG_DIRS_MODE)
filesystem.chmod(work_dir, constants.CONFIG_DIRS_MODE)
test_dir_ref = importlib_resources.files(pkg).joinpath("testdata").joinpath(test_dir)
with importlib_resources.as_file(test_dir_ref) as path:
test_dir_ref = importlib.resources.files(pkg).joinpath("testdata").joinpath(test_dir)
with importlib.resources.as_file(test_dir_ref) as path:
shutil.copytree(
path, os.path.join(temp_dir, test_dir), symlinks=True)

View file

@ -3,6 +3,7 @@ import atexit
from contextlib import ExitStack
import copy
from importlib import reload as reload_module
import importlib.resources
import io
import logging
import multiprocessing
@ -38,11 +39,6 @@ from certbot.compat import os
from certbot.display import util as display_util
from certbot.plugins import common
if sys.version_info >= (3, 9): # pragma: no cover
import importlib.resources as importlib_resources
else: # pragma: no cover
import importlib_resources
class DummyInstaller(common.Installer):
"""Dummy installer plugin for test purpose."""
@ -84,14 +80,14 @@ def vector_path(*names: str) -> str:
"""Path to a test vector."""
_file_manager = ExitStack()
atexit.register(_file_manager.close)
vector_ref = importlib_resources.files(__package__).joinpath('testdata', *names)
path = _file_manager.enter_context(importlib_resources.as_file(vector_ref))
vector_ref = importlib.resources.files(__package__).joinpath('testdata', *names)
path = _file_manager.enter_context(importlib.resources.as_file(vector_ref))
return str(path)
def load_vector(*names: str) -> bytes:
"""Load contents of a test vector."""
vector_ref = importlib_resources.files(__package__).joinpath('testdata', *names)
vector_ref = importlib.resources.files(__package__).joinpath('testdata', *names)
data = vector_ref.read_bytes()
# Try at most to convert CRLF to LF when data is text
try:

View file

@ -32,7 +32,6 @@ install_requires = [
'configobj>=5.0.6',
'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 2+ may introduce backward incompatible changes by droping usage of
# deprecated PyOpenSSL APIs.

View file

@ -32,7 +32,6 @@ google-auth==2.16.0 ; python_version >= "3.9" and python_version < "3.10"
httplib2==0.9.2 ; python_version >= "3.9" and python_version < "3.10"
idna==2.6 ; python_version >= "3.9" and python_version < "3.10"
importlib-metadata==4.6.4 ; python_version >= "3.9" and python_version < "3.10"
importlib-resources==6.4.5 ; python_version >= "3.9" and python_version < "3.10"
iniconfig==2.0.0 ; python_version >= "3.9" and python_version < "3.10"
ipaddress==1.0.16 ; python_version >= "3.9" and python_version < "3.10"
isort==5.13.2 ; python_version >= "3.9" and python_version < "3.10"

View file

@ -8,15 +8,15 @@
alabaster==0.7.16 ; python_version >= "3.9" and python_version < "4.0"
apacheconfig==0.3.2 ; python_version >= "3.9" and python_version < "4.0"
astroid==3.0.3 ; python_version >= "3.9" and python_version < "4.0"
asttokens==2.4.1 ; python_version >= "3.9" and python_version < "4.0"
asttokens==3.0.0 ; python_version >= "3.9" and python_version < "4.0"
attrs==24.2.0 ; python_version >= "3.9" and python_version < "4.0"
azure-core==1.32.0 ; python_version >= "3.9" and python_version < "4.0"
azure-devops==7.1.0b4 ; python_version >= "3.9" and python_version < "4.0"
babel==2.16.0 ; python_version >= "3.9" and python_version < "4.0"
bcrypt==4.2.1 ; python_version >= "3.9" and python_version < "4.0"
beautifulsoup4==4.12.3 ; python_version >= "3.9" and python_version < "4.0"
boto3==1.35.69 ; python_version >= "3.9" and python_version < "4.0"
botocore==1.35.69 ; python_version >= "3.9" and python_version < "4.0"
boto3==1.35.74 ; python_version >= "3.9" and python_version < "4.0"
botocore==1.35.74 ; python_version >= "3.9" and python_version < "4.0"
build==1.2.2.post1 ; python_version >= "3.9" and python_version < "4.0"
cachecontrol==0.14.1 ; python_version >= "3.9" and python_version < "4.0"
cachetools==5.5.0 ; python_version >= "3.9" and python_version < "4.0"
@ -46,7 +46,7 @@ exceptiongroup==1.2.2 ; python_version >= "3.9" and python_version < "3.11"
execnet==2.1.1 ; python_version >= "3.9" and python_version < "4.0"
executing==2.1.0 ; python_version >= "3.9" and python_version < "4.0"
fabric==3.2.2 ; python_version >= "3.9" and python_version < "4.0"
fastjsonschema==2.20.0 ; python_version >= "3.9" and python_version < "4.0"
fastjsonschema==2.21.1 ; python_version >= "3.9" and python_version < "4.0"
filelock==3.16.1 ; python_version >= "3.9" and python_version < "4.0"
google-api-core==2.23.0 ; python_version >= "3.9" and python_version < "4.0"
google-api-python-client==2.154.0 ; python_version >= "3.9" and python_version < "4.0"
@ -56,8 +56,7 @@ googleapis-common-protos==1.66.0 ; python_version >= "3.9" and python_version <
httplib2==0.22.0 ; python_version >= "3.9" and python_version < "4.0"
idna==3.10 ; python_version >= "3.9" and python_version < "4.0"
imagesize==1.4.1 ; python_version >= "3.9" and python_version < "4.0"
importlib-metadata==8.5.0 ; python_version >= "3.9" and python_version < "4.0"
importlib-resources==6.4.5 ; python_version >= "3.9" and python_version < "4.0"
importlib-metadata==8.5.0 ; python_version >= "3.9" and python_version < "3.12"
iniconfig==2.0.0 ; python_version >= "3.9" and python_version < "4.0"
installer==0.7.0 ; python_version >= "3.9" and python_version < "4.0"
invoke==2.2.0 ; python_version >= "3.9" and python_version < "4.0"
@ -84,7 +83,7 @@ msgpack==1.1.0 ; python_version >= "3.9" and python_version < "4.0"
msrest==0.7.1 ; python_version >= "3.9" and python_version < "4.0"
mypy-extensions==1.0.0 ; python_version >= "3.9" and python_version < "4.0"
mypy==1.9.0 ; python_version >= "3.9" and python_version < "4.0"
nh3==0.2.18 ; python_version >= "3.9" and python_version < "4.0"
nh3==0.2.19 ; python_version >= "3.9" and python_version < "4.0"
oauthlib==3.2.2 ; python_version >= "3.9" and python_version < "4.0"
packaging==24.2 ; python_version >= "3.9" and python_version < "4.0"
paramiko==3.5.0 ; python_version >= "3.9" and python_version < "4.0"
@ -92,7 +91,7 @@ parsedatetime==2.6 ; python_version >= "3.9" and python_version < "4.0"
parso==0.8.4 ; python_version >= "3.9" and python_version < "4.0"
pexpect==4.9.0 ; python_version >= "3.9" and python_version < "4.0"
pip==24.3.1 ; python_version >= "3.9" and python_version < "4.0"
pkginfo==1.10.0 ; python_version >= "3.9" and python_version < "4.0"
pkginfo==1.12.0 ; python_version >= "3.9" and python_version < "4.0"
platformdirs==4.3.6 ; python_version >= "3.9" and python_version < "4.0"
pluggy==1.5.0 ; python_version >= "3.9" and python_version < "4.0"
ply==3.11 ; python_version >= "3.9" and python_version < "4.0"
@ -101,7 +100,7 @@ poetry-plugin-export==1.8.0 ; python_version >= "3.9" and python_version < "4.0"
poetry==1.8.4 ; python_version >= "3.9" and python_version < "4.0"
prompt-toolkit==3.0.48 ; python_version >= "3.9" and python_version < "4.0"
proto-plus==1.25.0 ; python_version >= "3.9" and python_version < "4.0"
protobuf==5.28.3 ; python_version >= "3.9" and python_version < "4.0"
protobuf==5.29.0 ; python_version >= "3.9" and python_version < "4.0"
ptyprocess==0.7.0 ; python_version >= "3.9" and python_version < "4.0"
pure-eval==0.2.3 ; python_version >= "3.9" and python_version < "4.0"
pyasn1-modules==0.4.1 ; python_version >= "3.9" and python_version < "4.0"
@ -119,7 +118,7 @@ pyproject-hooks==1.2.0 ; python_version >= "3.9" and python_version < "4.0"
pyrfc3339==2.0.1 ; python_version >= "3.9" and python_version < "4.0"
pytest-cov==6.0.0 ; python_version >= "3.9" and python_version < "4.0"
pytest-xdist==3.6.1 ; python_version >= "3.9" and python_version < "4.0"
pytest==8.3.3 ; python_version >= "3.9" and python_version < "4.0"
pytest==8.3.4 ; python_version >= "3.9" and python_version < "4.0"
python-augeas==1.1.0 ; python_version >= "3.9" and python_version < "4.0"
python-dateutil==2.9.0.post0 ; python_version >= "3.9" and python_version < "4.0"
python-digitalocean==1.17.0 ; python_version >= "3.9" and python_version < "4.0"
@ -157,29 +156,29 @@ sphinxcontrib-qthelp==2.0.0 ; python_version >= "3.9" and python_version < "4.0"
sphinxcontrib-serializinghtml==2.0.0 ; python_version >= "3.9" and python_version < "4.0"
stack-data==0.6.3 ; python_version >= "3.9" and python_version < "4.0"
tldextract==5.1.3 ; python_version >= "3.9" and python_version < "4.0"
tomli==2.1.0 ; python_version >= "3.9" and python_full_version <= "3.11.0a6"
tomli==2.2.1 ; python_version >= "3.9" and python_full_version <= "3.11.0a6"
tomlkit==0.13.2 ; python_version >= "3.9" and python_version < "4.0"
tox==4.23.2 ; python_version >= "3.9" and python_version < "4.0"
traitlets==5.14.3 ; python_version >= "3.9" and python_version < "4.0"
trove-classifiers==2024.10.21.16 ; python_version >= "3.9" and python_version < "4.0"
twine==5.1.1 ; python_version >= "3.9" and python_version < "4.0"
twine==6.0.1 ; python_version >= "3.9" and python_version < "4.0"
types-cffi==1.16.0.20240331 ; python_version >= "3.9" and python_version < "4.0"
types-httplib2==0.22.0.20240310 ; python_version >= "3.9" and python_version < "4.0"
types-pyopenssl==24.1.0.20240722 ; python_version >= "3.9" and python_version < "4.0"
types-pyrfc3339==2.0.1.20241107 ; python_version >= "3.9" and python_version < "4.0"
types-python-dateutil==2.9.0.20241003 ; python_version >= "3.9" and python_version < "4.0"
types-pytz==2024.2.0.20241003 ; python_version >= "3.9" and python_version < "4.0"
types-pywin32==308.0.0.20241121 ; python_version >= "3.9" and python_version < "4.0"
types-pywin32==308.0.0.20241128 ; python_version >= "3.9" and python_version < "4.0"
types-requests==2.31.0.6 ; python_version >= "3.9" and python_version < "4.0"
types-setuptools==75.5.0.20241122 ; python_version >= "3.9" and python_version < "4.0"
types-setuptools==75.6.0.20241126 ; python_version >= "3.9" and python_version < "4.0"
types-urllib3==1.26.25.14 ; python_version >= "3.9" and python_version < "4.0"
typing-extensions==4.12.2 ; python_version >= "3.9" and python_version < "4.0"
uritemplate==4.1.1 ; python_version >= "3.9" and python_version < "4.0"
urllib3==1.26.20 ; python_version >= "3.9" and python_version < "4.0"
virtualenv==20.27.1 ; python_version >= "3.9" and python_version < "4.0"
virtualenv==20.28.0 ; python_version >= "3.9" and python_version < "4.0"
wcwidth==0.2.13 ; python_version >= "3.9" and python_version < "4.0"
wheel==0.45.1 ; python_version >= "3.9" and python_version < "4.0"
wrapt==1.17.0 ; python_version >= "3.9" and python_version < "4.0"
xattr==1.1.0 ; python_version >= "3.9" and python_version < "4.0" and sys_platform == "darwin"
yarg==0.1.10 ; python_version >= "3.9" and python_version < "4.0"
zipp==3.21.0 ; python_version >= "3.9" and python_version < "4.0"
zipp==3.21.0 ; python_version >= "3.9" and python_version < "3.12"