use pep585 types everywhere and add a test (#10414)

this is the final part of
https://github.com/certbot/certbot/issues/10195. this fixes
https://github.com/certbot/certbot/issues/10195

the changes in the first commit were done automatically with the
command:
```
ruff check --fix --extend-select UP006 --unsafe-fixes
```
the second commit configures ruff to check for this to avoid regressions

thanks for bearing with me thru these somewhat large automatically
generated PRs ohemorange 🙏
This commit is contained in:
Brad Warren
2025-08-12 16:56:45 -07:00
committed by GitHub
parent 27b344c8d8
commit d5a2e9227c
32 changed files with 161 additions and 219 deletions
@@ -3,8 +3,6 @@ import argparse
import os
import shutil
import subprocess
from typing import Set
from typing import Tuple
from unittest import mock
from certbot import configuration
@@ -80,7 +78,7 @@ def _get_server_root(config: str) -> str:
return os.path.join(config, subdirs[0].rstrip())
def _get_names(config: str) -> Tuple[Set[str], Set[str]]:
def _get_names(config: str) -> tuple[set[str], set[str]]:
"""Returns all and testable domain names in config"""
all_names = set()
non_ip_names = set()
@@ -6,12 +6,8 @@ import os
import shutil
import tempfile
from typing import Iterable
from typing import List
from typing import Optional
from typing import overload
from typing import Set
from typing import Tuple
from typing import Type
from typing import Union
from acme import challenges
@@ -50,8 +46,8 @@ class Proxy(interfaces.ConfiguratorProxy):
self.http_port = 80
self.https_port = 443
self._configurator: common.Configurator
self._all_names: Optional[Set[str]] = None
self._test_names: Optional[Set[str]] = None
self._all_names: Optional[set[str]] = None
self._test_names: Optional[set[str]] = None
def has_more_configs(self) -> bool:
"""Returns true if there are more configs to test"""
@@ -70,14 +66,14 @@ class Proxy(interfaces.ConfiguratorProxy):
@overload
def copy_certs_and_keys(self, cert_path: str, key_path: str,
chain_path: str) -> Tuple[str, str, str]: ...
chain_path: str) -> tuple[str, str, str]: ...
@overload
def copy_certs_and_keys(self, cert_path: str, key_path: str,
chain_path: Optional[str]) -> Tuple[str, str, Optional[str]]: ...
chain_path: Optional[str]) -> tuple[str, str, Optional[str]]: ...
def copy_certs_and_keys(self, cert_path: str, key_path: str,
chain_path: Optional[str] = None) -> Tuple[str, str, Optional[str]]:
chain_path: Optional[str] = None) -> tuple[str, str, Optional[str]]:
"""Copies certs and keys into the temporary directory"""
cert_and_key_dir = os.path.join(self._temp_dir, "certs_and_keys")
if not os.path.isdir(cert_and_key_dir):
@@ -94,13 +90,13 @@ class Proxy(interfaces.ConfiguratorProxy):
return cert, key, chain
def get_all_names_answer(self) -> Set[str]:
def get_all_names_answer(self) -> set[str]:
"""Returns the set of domain names that the plugin should find"""
if self._all_names:
return self._all_names
raise errors.Error("No configuration file loaded")
def get_testable_domain_names(self) -> Set[str]:
def get_testable_domain_names(self) -> set[str]:
"""Returns the set of domain names that can be tested against"""
if self._test_names:
return self._test_names
@@ -115,20 +111,20 @@ class Proxy(interfaces.ConfiguratorProxy):
self._configurator.deploy_cert(
domain, cert_path, key_path, chain_path, fullchain_path)
def cleanup(self, achalls: List[AnnotatedChallenge]) -> None:
def cleanup(self, achalls: list[AnnotatedChallenge]) -> None:
self._configurator.cleanup(achalls)
def config_test(self) -> None:
self._configurator.config_test()
def enhance(self, domain: str, enhancement: str,
options: Optional[Union[List[str], str]] = None) -> None:
options: Optional[Union[list[str], str]] = None) -> None:
self._configurator.enhance(domain, enhancement, options)
def get_all_names(self) -> Iterable[str]:
return self._configurator.get_all_names()
def get_chall_pref(self, domain: str) -> Iterable[Type[Challenge]]:
def get_chall_pref(self, domain: str) -> Iterable[type[Challenge]]:
return self._configurator.get_chall_pref(domain)
@classmethod
@@ -138,7 +134,7 @@ class Proxy(interfaces.ConfiguratorProxy):
def more_info(self) -> str:
return self._configurator.more_info()
def perform(self, achalls: List[AnnotatedChallenge]) -> List[challenges.ChallengeResponse]:
def perform(self, achalls: list[AnnotatedChallenge]) -> list[challenges.ChallengeResponse]:
return self._configurator.perform(achalls)
def prepare(self) -> None:
@@ -156,5 +152,5 @@ class Proxy(interfaces.ConfiguratorProxy):
def save(self, title: Optional[str] = None, temporary: bool = False) -> None:
self._configurator.save(title, temporary)
def supported_enhancements(self) -> List[str]:
def supported_enhancements(self) -> list[str]:
return self._configurator.supported_enhancements()
@@ -2,8 +2,6 @@
import os
import shutil
import subprocess
from typing import Set
from typing import Tuple
from certbot import configuration
from certbot_compatibility_test import errors
@@ -64,9 +62,9 @@ def _get_server_root(config: str) -> str:
return os.path.join(config, subdirs[0].rstrip())
def _get_names(config: str) -> Tuple[Set[str], Set[str]]:
def _get_names(config: str) -> tuple[set[str], set[str]]:
"""Returns all and testable domain names in config"""
all_names: Set[str] = set()
all_names: set[str] = set()
for root, _dirs, files in os.walk(config):
for this_file in files:
update_names = _get_server_names(root, this_file)
@@ -75,7 +73,7 @@ def _get_names(config: str) -> Tuple[Set[str], Set[str]]:
return all_names, non_ip_names
def _get_server_names(root: str, filename: str) -> Set[str]:
def _get_server_names(root: str, filename: str) -> set[str]:
"""Returns all names in a config file path"""
all_names = set()
with open(os.path.join(root, filename)) as f:
@@ -3,7 +3,6 @@ from abc import ABCMeta
from abc import abstractmethod
import argparse
from typing import cast
from typing import Set
from certbot import interfaces
from certbot.configuration import NamespaceConfig
@@ -45,7 +44,7 @@ class PluginProxy(interfaces.Plugin, metaclass=ABCMeta):
"""Loads the next config and returns its name"""
@abstractmethod
def get_testable_domain_names(self) -> Set[str]:
def get_testable_domain_names(self) -> set[str]:
"""Returns the domain names that can be used in testing"""
@@ -57,7 +56,7 @@ class InstallerProxy(PluginProxy, interfaces.Installer, metaclass=ABCMeta):
"""Wraps a Certbot installer"""
@abstractmethod
def get_all_names_answer(self) -> Set[str]:
def get_all_names_answer(self) -> set[str]:
"""Returns all names that should be found by the installer"""
@@ -10,13 +10,9 @@ import sys
import tempfile
import time
from typing import Any
from typing import Dict
from typing import Generator
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple
from typing import Type
from cryptography.hazmat.primitives import serialization
from urllib3.util import connection
@@ -42,7 +38,7 @@ tests that the plugin supports are performed.
"""
PLUGINS: Dict[str, Type[common.Proxy]] = {"apache": a_common.Proxy, "nginx": n_common.Proxy}
PLUGINS: dict[str, type[common.Proxy]] = {"apache": a_common.Proxy, "nginx": n_common.Proxy}
logger = logging.getLogger(__name__)
@@ -103,9 +99,9 @@ def test_authenticator(plugin: common.Proxy, config: str, temp_dir: str) -> bool
return success
def _create_achalls(plugin: common.Proxy) -> List[achallenges.AnnotatedChallenge]:
def _create_achalls(plugin: common.Proxy) -> list[achallenges.AnnotatedChallenge]:
"""Returns a list of annotated challenges to test on plugin"""
achalls: List[achallenges.AnnotatedChallenge] = []
achalls: list[achallenges.AnnotatedChallenge] = []
names = plugin.get_testable_domain_names()
for domain in names:
prefs = plugin.get_chall_pref(domain)
@@ -145,7 +141,7 @@ def test_installer(args: argparse.Namespace, plugin: common.Proxy, config: str,
return names_match and success and good_rollback
def test_deploy_cert(plugin: common.Proxy, temp_dir: str, domains: List[str]) -> bool:
def test_deploy_cert(plugin: common.Proxy, temp_dir: str, domains: list[str]) -> bool:
"""Tests deploy_cert returning True if the tests are successful"""
cert = crypto_util.make_self_signed_cert(util.KEY, domains)
cert_path = os.path.join(temp_dir, "cert.pem")
@@ -187,7 +183,7 @@ def test_enhancements(plugin: common.Proxy, domains: Iterable[str]) -> bool:
"enhancements")
return False
domains_and_info: List[Tuple[str, List[bool]]] = [(domain, []) for domain in domains]
domains_and_info: list[tuple[str, list[bool]]] = [(domain, []) for domain in domains]
for domain, info in domains_and_info:
try:
@@ -390,7 +386,7 @@ def _fake_dns_resolution(resolved_ip: str) -> Generator[None, None, None]:
"""Monkey patch urllib3 to make any hostname be resolved to the provided IP"""
_original_create_connection = connection.create_connection
def _patched_create_connection(address: Tuple[str, int],
def _patched_create_connection(address: tuple[str, int],
*args: Any, **kwargs: Any) -> socket.socket:
_, port = address
return _original_create_connection((resolved_ip, port), *args, **kwargs)
@@ -5,7 +5,6 @@ import socket
from typing import cast
from typing import Mapping
from typing import Optional
from typing import Tuple
from typing import Union
from cryptography import x509
@@ -146,7 +145,7 @@ def _probe_sni(name: bytes, host: bytes, port: int = 443) -> x509.Certificate:
# Enables multi-path probing (selection
# of source interface). See `socket.creation_connection` for more
# info. Available only in Python 2.7+.
source_address: Tuple[str, int] = ('', 0)
source_address: tuple[str, int] = ('', 0)
socket_kwargs = {'source_address': source_address}
try:
@@ -157,7 +156,7 @@ def _probe_sni(name: bytes, host: bytes, port: int = 443) -> x509.Certificate:
source_address[1]
) if any(source_address) else ""
)
socket_tuple: Tuple[bytes, int] = (host, port)
socket_tuple: tuple[bytes, int] = (host, port)
sock = socket.create_connection(socket_tuple, **socket_kwargs) # type: ignore[arg-type]
except OSError as error:
raise acme_errors.Error(error)