Package: release.debian.org
Severity: normal
X-Debbugs-Cc: python-certbot-apa...@packages.debian.org, hlieber...@debian.org
Control: affects -1 + src:python-certbot-apache
User: release.debian....@packages.debian.org
Usertags: unblock
User: hlieber...@debian.org
Usertags: trixie-certbot

Please unblock package python-certbot-apache

[ Reason ]

The certbot 2.x series is end of life and will not receive further updates or
backports of changes.
(https://github.com/certbot/certbot/wiki/Architectural-Decision-Records-2025#-update-to-certbots-version-policy-and-end-of-life-support-on-previous-major-versions)
By far and away, the primary purpose of certbot is to receive certificates from
Let's Encrypt, and the Let's Encrypt team are planning API changes in 2025 which
will break the issuance of TLS certificates for people using the Certbot 2.x
series.

[ Impact ]

If the unblock is not granted, certbot will suddenly stop working at
some point in the future and users' TLS certificates will expire. Because
certbot tends to be used as a set-it-and-forget-it system, and Let's Encrypt has
recently disabled their email notifications, users' websites and applications
may suddenly be unavailable to users and/or vulnerable to MitM.

[ Tests ]

Certbot's two primary plugins (python-certbot-apache, python-certbot-nginx) and
the main utility (python-certbot) have a test harness which exercises the entire
process of getting a certificate against a test environment. This provides very
high confidence that those packages are still working, and that the libraries
which they depend on (python-josepy, python-acme) are in good health. These
tests pass cleanly on ci.d.n for all three invocations.

The dns plugin packages (python-certbot-dns-*) are substantially less
complicated than the other certbot packages and primarily handle communication
with various companies' API layers. Those are unlikely to have broken because of
the changes to certbot's internals; the primary way in which those packages
break are due to API changes on the providers' ends.

[ Risks ]

Upgrading the packages across major versions comes with risks, certainly, but
there is little in the way of alternative. The changes are too complex for me to
be willing to attempt to backport, and in a security critical application, I am
even more reticent than I normally would be. I recognize the late application
introduces even more risk --- and rightfully, I'm sure no small amount of
annoyance --- but it is where we've ended up.

[ Checklist ]
  [X] all changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in testing

[ Other info ]

This is one of a series of identical unblock requests with only
package names and debdiffs differing.

unblock python-certbot-apache/4.0.0-2
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/apache_util.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/apache_util.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/apache_util.py        
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/apache_util.py 
2025-04-07 18:03:33.000000000 -0400
@@ -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 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 @@
     """
     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)))
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/configurator.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/configurator.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/configurator.py       
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/configurator.py        
2025-04-07 18:03:33.000000000 -0400
@@ -271,10 +271,10 @@
         self.parser: parser.ApacheParser
         self.parser_root: Optional[dualparser.DualBlockNode] = None
         self.version = version
-        self._openssl_version = openssl_version
+        self._openssl_version: Optional[str] = openssl_version
         self.vhosts: List[obj.VirtualHost]
         self.options = copy.deepcopy(self.OS_DEFAULTS)
-        self._enhance_func: Dict[str, Callable] = {
+        self._enhance_func: Dict[str, Callable[[obj.VirtualHost, Any], None]] 
= {
             "redirect": self._enable_redirect,
             "ensure-http-header": self._set_http_header,
             "staple-ocsp": self._enable_ocsp_stapling,
@@ -295,7 +295,7 @@
         try:
             with open(ssl_module_location, mode="rb") as f:
                 contents = f.read()
-        except IOError as error:
+        except OSError as error:
             logger.debug(str(error), exc_info=True)
             return None
         return contents
@@ -599,7 +599,7 @@
         """Prompts user to choose vhosts to install a wildcard certificate 
for"""
 
         # Get all vhosts that are covered by the wildcard domain
-        vhosts: List = self._vhosts_for_wildcard(domain)
+        vhosts: List[obj.VirtualHost] = self._vhosts_for_wildcard(domain)
 
         # Go through the vhosts, making sure that we cover all the names
         # present, but preferring the SSL vhosts
@@ -928,7 +928,7 @@
             try:
                 socket.inet_aton(addr.get_addr())
                 return socket.gethostbyaddr(addr.get_addr())[0]
-            except (socket.error, socket.herror, socket.timeout):
+            except (OSError, socket.herror, socket.timeout):
                 pass
 
         return ""
@@ -994,9 +994,7 @@
         for arg in args:
             arg_value = self.parser.get_arg(arg)
             if arg_value is not None:
-                addr = obj.Addr.fromstring(arg_value)
-                if addr is not None:
-                    addrs.add(addr)
+                addrs.add(obj.Addr.fromstring(arg_value))
         is_ssl = False
 
         if self.parser.find_dir("SSLEngine", "on", start=path, exclude=False):
@@ -1126,9 +1124,7 @@
         """
         addrs = set()
         for param in node.parameters:
-            addr = obj.Addr.fromstring(param)
-            if addr:
-                addrs.add(addr)
+            addrs.add(obj.Addr.fromstring(param))
 
         is_ssl = False
         # Exclusion to match the behavior in get_virtual_hosts_v2
@@ -1523,7 +1519,7 @@
             # activation (it's not included as default)
             if not self.parser.parsed_in_current(ssl_fp):
                 self.parser.parse_file(ssl_fp)
-        except IOError:
+        except OSError:
             logger.critical("Error writing/reading to file in make_vhost_ssl", 
exc_info=True)
             raise errors.PluginError("Unable to write/read in make_vhost_ssl")
 
@@ -1646,10 +1642,9 @@
         for addr in ssl_addr_p:
             old_addr = obj.Addr.fromstring(
                 str(self.parser.get_arg(addr)))
-            if old_addr:
-                ssl_addr = old_addr.get_addr_obj("443")
-                self.parser.aug.set(addr, str(ssl_addr))
-                ssl_addrs.add(ssl_addr)
+            ssl_addr = old_addr.get_addr_obj("443")
+            self.parser.aug.set(addr, str(ssl_addr))
+            ssl_addrs.add(ssl_addr)
 
         return ssl_addrs
 
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/constants.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/constants.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/constants.py  
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/constants.py   
2025-04-07 18:03:33.000000000 -0400
@@ -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 @@
     # 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"""
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/dualparser.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/dualparser.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/dualparser.py 
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/dualparser.py  
2025-04-07 18:03:33.000000000 -0400
@@ -6,7 +6,6 @@
 from typing import Optional
 from typing import Set
 from typing import Tuple
-from typing import Type
 from typing import TYPE_CHECKING
 from typing import TypeVar
 
@@ -21,7 +20,8 @@
 
 GenericAugeasParserNode = TypeVar("GenericAugeasParserNode", 
bound="AugeasParserNode")
 GenericApacheParserNode = TypeVar("GenericApacheParserNode", 
bound="ApacheParserNode")
-GenericDualNode = TypeVar("GenericDualNode", bound="DualNodeBase")
+# Circular imports needs Any, see https://github.com/python/mypy/issues/11910
+GenericDualNode = TypeVar("GenericDualNode", bound="DualNodeBase[Any, Any]")
 
 
 class DualNodeBase(Generic[GenericAugeasParserNode, GenericApacheParserNode]):
@@ -53,11 +53,11 @@
             assertions.assertEqualSimple(firstval, secondval)
         return firstval
 
-    def find_ancestors(self, name: str) -> List["DualNodeBase"]:
+    def find_ancestors(self, name: str) -> List["DualBlockNode"]:
         """ Traverses the ancestor tree and returns ancestors matching name """
         return self._find_helper(DualBlockNode, "find_ancestors", name)
 
-    def _find_helper(self, nodeclass: Type[GenericDualNode], findfunc: str, 
search: str,
+    def _find_helper(self, nodeclass: type[GenericDualNode], findfunc: str, 
search: str,
                      **kwargs: Any) -> List[GenericDualNode]:
         """A helper for find_* functions. The function specific attributes 
should
         be passed as keyword arguments.
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/http_01.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/http_01.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/http_01.py    
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/http_01.py     
2025-04-07 18:03:33.000000000 -0400
@@ -24,10 +24,10 @@
     """Class that performs HTTP-01 challenges within the Apache 
configurator."""
 
     CONFIG_TEMPLATE24_PRE = """\
-        RewriteEngine on
         RewriteRule ^/\\.well-known/acme-challenge/([A-Za-z0-9-_=]+)$ {0}/$1 
[END]
     """
     CONFIG_TEMPLATE24_POST = """\
+        RewriteEngine on
         <Directory {0}>
             Require all granted
         </Directory>
@@ -172,6 +172,7 @@
 
     def _set_up_challenge(self, achall: KeyAuthorizationAnnotatedChallenge
                           ) -> KeyAuthorizationChallengeResponse:
+        response: KeyAuthorizationChallengeResponse
         response, validation = achall.response_and_validation()
 
         name: str = os.path.join(self.challenge_dir, 
achall.chall.encode("token"))
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/obj.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/obj.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/obj.py        
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/obj.py 2025-04-07 
18:03:33.000000000 -0400
@@ -3,7 +3,6 @@
 from typing import Any
 from typing import Iterable
 from typing import Optional
-from typing import Pattern
 from typing import Set
 from typing import Union
 
@@ -126,7 +125,7 @@
 
     """
     # ?: is used for not returning enclosed characters
-    strip_name: Pattern = re.compile(r"^(?:.+://)?([^ :$]*)")
+    strip_name: re.Pattern[str] = re.compile(r"^(?:.+://)?([^ :$]*)")
 
     def __init__(self, filepath: str, path: str, addrs: Set["Addr"], ssl: bool,
                  enabled: bool, name: Optional[str] = None, aliases: 
Optional[Set[str]] = None,
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/parser.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/parser.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/parser.py     
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/parser.py      
2025-04-07 18:03:33.000000000 -0400
@@ -9,7 +9,6 @@
 from typing import List
 from typing import Mapping
 from typing import Optional
-from typing import Pattern
 from typing import Set
 from typing import Tuple
 from typing import TYPE_CHECKING
@@ -43,7 +42,7 @@
         default - user config file, name - NameVirtualHost,
 
     """
-    arg_var_interpreter: Pattern = re.compile(r"\$\{[^ \}]*}")
+    arg_var_interpreter: re.Pattern[str] = re.compile(r"\$\{[^ \}]*}")
     fnmatch_chars: Set[str] = {"*", "?", "\\", "[", "]"}
 
     # pylint: disable=unused-argument
@@ -127,7 +126,7 @@
 
         self.aug.set("/test/path/testing/arg", "aRgUMeNT")
         try:
-            matches = self.aug.match(
+            matches: List[str] = self.aug.match(
                 "/test//*[self::arg=~regexp('argument', 'i')]")
         except RuntimeError:
             self.aug.remove("/test/path")
@@ -153,7 +152,7 @@
         try:
             # This is a noop save
             self.aug.save()
-        except (RuntimeError, IOError):
+        except (OSError, RuntimeError):
             self._log_save_errors(ex_errs)
             # Erase Save Notes
             self.configurator.save_notes = ""
@@ -198,7 +197,7 @@
         ex_errs = self.aug.match("/augeas//error")
         try:
             self.aug.save()
-        except IOError:
+        except OSError:
             self._log_save_errors(ex_errs)
             raise
 
@@ -390,7 +389,7 @@
         :rtype: str
 
         """
-        if_mods = self.aug.match(("%s/IfModule/*[self::arg='%s']" %
+        if_mods: List[str] = self.aug.match(("%s/IfModule/*[self::arg='%s']" %
                                   (aug_conf_path, mod)))
         if not if_mods:
             return self.create_ifmod(aug_conf_path, mod)
@@ -578,7 +577,7 @@
         This also converts all variables and parameters appropriately.
 
         """
-        value = self.aug.get(match)
+        value: str = self.aug.get(match)
 
         # No need to strip quotes for variables, as apache2ctl already does
         # this, but we do need to strip quotes for all normal arguments.
diff -Nru 
python-certbot-apache-2.11.0/certbot_apache/_internal/tests/configurator_test.py
 python-certbot-apache-4.0.0/certbot_apache/_internal/tests/configurator_test.py
--- 
python-certbot-apache-2.11.0/certbot_apache/_internal/tests/configurator_test.py
    2024-06-05 17:34:02.000000000 -0400
+++ 
python-certbot-apache-4.0.0/certbot_apache/_internal/tests/configurator_test.py 
    2025-04-07 18:03:33.000000000 -0400
@@ -174,9 +174,9 @@
         assert "certbot.demo" in names
 
     def test_get_bad_path(self):
-        assert apache_util.get_file_path(None) == None
-        assert apache_util.get_file_path("nonexistent") == None
-        assert self.config._create_vhost("nonexistent") == None # pylint: 
disable=protected-access
+        assert apache_util.get_file_path(None) is None
+        assert apache_util.get_file_path("nonexistent") is None
+        assert self.config._create_vhost("nonexistent") is None # pylint: 
disable=protected-access
 
     def test_get_aug_internal_path(self):
         from certbot_apache._internal.apache_util import get_internal_aug_path
@@ -303,7 +303,7 @@
         # pylint: disable=protected-access
         assert self.vh_truth[3] == self.config._find_best_vhost("certbot.demo")
         assert self.vh_truth[0] == 
self.config._find_best_vhost("encryption-example.demo")
-        assert self.config._find_best_vhost("does-not-exist.com") == None
+        assert self.config._find_best_vhost("does-not-exist.com") is None
 
     def test_find_best_vhost_variety(self):
         # pylint: disable=protected-access
@@ -1417,7 +1417,7 @@
         self.config.parser.aug.match.side_effect = RuntimeError
         path = 
"debian_apache_2_4/augeas_vhosts/apache2/sites-available/old-and-default.conf"
         chosen_vhost = self.config._create_vhost(path)
-        assert None == chosen_vhost
+        assert chosen_vhost is None
 
     def test_choosevhost_works(self):
         path = 
"debian_apache_2_4/augeas_vhosts/apache2/sites-available/old-and-default.conf"
@@ -1518,16 +1518,11 @@
         with_index_1 = ["/path[1]/section[1]"]
         without_index = ["/path/section"]
         with_index_2 = ["/path[2]/section[2]"]
-        assert self.config._get_new_vh_path(without_index,
-                                                      with_index_1) == \
-                         None
-        assert self.config._get_new_vh_path(without_index,
-                                                      with_index_2) == \
-                         with_index_2[0]
+        assert self.config._get_new_vh_path(without_index, with_index_1) is 
None
+        assert self.config._get_new_vh_path(without_index, with_index_2) == 
with_index_2[0]
 
         both = with_index_1 + with_index_2
-        assert self.config._get_new_vh_path(without_index, both) == \
-                         with_index_2[0]
+        assert self.config._get_new_vh_path(without_index, both) == 
with_index_2[0]
 
     @mock.patch("certbot_apache._internal.configurator.display_util.notify")
     def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_notify):
@@ -1658,15 +1653,12 @@
         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
@@ -1723,14 +1715,14 @@
 
         self.config._openssl_version = None
         with 
mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
-            assert self.config.openssl_version() == None
+            assert self.config.openssl_version() is None
             assert "Could not find ssl_module" in mock_log.call_args[0][0]
 
         # When no ssl_module is present at all
         self.config._openssl_version = None
         assert "ssl_module" not in self.config.parser.modules
         with 
mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
-            assert self.config.openssl_version() == None
+            assert self.config.openssl_version() is None
             assert "Could not find ssl_module" in mock_log.call_args[0][0]
 
         # When ssl_module is statically linked but --apache-bin not provided
@@ -1738,13 +1730,13 @@
         self.config.options.bin = None
         self.config.parser.modules['ssl_module'] = None
         with 
mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
-            assert self.config.openssl_version() == None
+            assert self.config.openssl_version() is None
             assert "ssl_module is statically linked but" in 
mock_log.call_args[0][0]
 
         self.config.parser.modules['ssl_module'] = "/fake/path"
         with 
mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
             # Check that correct logger.warning was printed
-            assert self.config.openssl_version() == None
+            assert self.config.openssl_version() is None
             assert "Unable to read" in mock_log.call_args[0][0]
 
         contents_missing_openssl = b"these contents won't match the regex"
@@ -1753,7 +1745,7 @@
             mock_omf.return_value = contents_missing_openssl
             with 
mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
                 # Check that correct logger.warning was printed
-                assert self.config.openssl_version() == None
+                assert self.config.openssl_version() is None
                 assert "Could not find OpenSSL" in mock_log.call_args[0][0]
 
     def test_open_module_file(self):
diff -Nru 
python-certbot-apache-2.11.0/certbot_apache/_internal/tests/http_01_test.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/tests/http_01_test.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/tests/http_01_test.py 
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/tests/http_01_test.py  
2025-04-07 18:03:33.000000000 -0400
@@ -209,7 +209,7 @@
         with open(self.http.challenge_conf_post) as f:
             post_conf_contents = f.read()
 
-        assert "RewriteEngine on" in pre_conf_contents
+        assert "RewriteEngine on" in post_conf_contents
         assert "RewriteRule" in pre_conf_contents
 
         assert self.http.challenge_dir in post_conf_contents
diff -Nru python-certbot-apache-2.11.0/certbot_apache/_internal/tests/util.py 
python-certbot-apache-4.0.0/certbot_apache/_internal/tests/util.py
--- python-certbot-apache-2.11.0/certbot_apache/_internal/tests/util.py 
2024-06-05 17:34:02.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache/_internal/tests/util.py  
2025-04-07 18:03:33.000000000 -0400
@@ -1,5 +1,8 @@
 """Common utilities for certbot_apache."""
 import shutil
+from typing import List
+from typing import Optional
+from typing import Tuple
 import unittest
 from unittest import mock
 
@@ -16,9 +19,10 @@
 
 class ApacheTest(unittest.TestCase):
 
-    def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts",
-              config_root="debian_apache_2_4/multiple_vhosts/apache2",
-              
vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"):
+    def setUp(self, test_dir: str = "debian_apache_2_4/multiple_vhosts",
+              config_root: str = "debian_apache_2_4/multiple_vhosts/apache2",
+              vhost_root: str = 
"debian_apache_2_4/multiple_vhosts/apache2/sites-available"
+              ) -> None:
         # pylint: disable=arguments-differ
         self.temp_dir, self.config_dir, self.work_dir = common.dir_setup(
             test_dir=test_dir,
@@ -27,7 +31,7 @@
         self.config_path = os.path.join(self.temp_dir, config_root)
         self.vhost_path = os.path.join(self.temp_dir, vhost_root)
 
-        self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector(
+        self.rsa512jwk = jose.jwk.JWKRSA.load(test_util.load_vector(
             "rsa512_key.pem"))
 
         self.config = get_apache_configurator(self.config_path, vhost_root,
@@ -50,7 +54,7 @@
                     os.path.pardir, "sites-available", vhost_basename)
                 os.symlink(target, vhost)
 
-    def tearDown(self):
+    def tearDown(self) -> None:
         shutil.rmtree(self.temp_dir)
         shutil.rmtree(self.config_dir)
         shutil.rmtree(self.work_dir)
@@ -58,9 +62,10 @@
 
 class ParserTest(ApacheTest):
 
-    def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts",
-              config_root="debian_apache_2_4/multiple_vhosts/apache2",
-              
vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"):
+    def setUp(self, test_dir: str = "debian_apache_2_4/multiple_vhosts",
+              config_root: str = "debian_apache_2_4/multiple_vhosts/apache2",
+              vhost_root: str = 
"debian_apache_2_4/multiple_vhosts/apache2/sites-available"
+              ) -> None:
         super().setUp(test_dir, config_root, vhost_root)
 
         from certbot_apache._internal.parser import ApacheParser
@@ -73,12 +78,12 @@
 
 
 def get_apache_configurator(
-        config_path, vhost_path,
-        config_dir, work_dir, version=(2, 4, 7),
-        os_info="generic",
-        conf_vhost_path=None,
-        use_parsernode=False,
-        openssl_version="1.1.1a"):
+        config_path: str, vhost_path: str,
+        config_dir: str, work_dir: str, version: Tuple[int, int, int] = (2, 4, 
7),
+        os_info: str = "generic",
+        conf_vhost_path: Optional[str] = None,
+        use_parsernode: bool = False,
+        openssl_version: str = "1.1.1a") -> configurator.ApacheConfigurator:
     """Create an Apache Configurator with the specified options.
 
     :param conf: Function that returns binary paths. self.conf in Configurator
@@ -124,7 +129,7 @@
     return config
 
 
-def get_vh_truth(temp_dir, config_name):
+def get_vh_truth(temp_dir: str, config_name: str) -> 
Optional[List[obj.VirtualHost]]:
     """Return the ground truth for the specified directory."""
     if config_name == "debian_apache_2_4/multiple_vhosts":
         prefix = os.path.join(
@@ -146,13 +151,13 @@
                 os.path.join(prefix, "000-default.conf"),
                 os.path.join(aug_pre, "000-default.conf/VirtualHost"),
                 {obj.Addr.fromstring("*:80"),
-                     obj.Addr.fromstring("[::]:80")},
+                    obj.Addr.fromstring("[::]:80")},
                 False, True, "ip-172-30-0-17"),
             obj.VirtualHost(
                 os.path.join(prefix, "certbot.conf"),
                 os.path.join(aug_pre, "certbot.conf/VirtualHost"),
                 {obj.Addr.fromstring("*:80")}, False, True,
-                "certbot.demo", aliases=["www.certbot.demo"]),
+                "certbot.demo", aliases={"www.certbot.demo"}),
             obj.VirtualHost(
                 os.path.join(prefix, "mod_macro-example.conf"),
                 os.path.join(aug_pre,
@@ -168,7 +173,7 @@
                 os.path.join(prefix, "wildcard.conf"),
                 os.path.join(aug_pre, "wildcard.conf/VirtualHost"),
                 {obj.Addr.fromstring("*:80")}, False, True,
-                "ip-172-30-0-17", aliases=["*.blue.purple.com"]),
+                "ip-172-30-0-17", aliases={"*.blue.purple.com"}),
             obj.VirtualHost(
                 os.path.join(prefix, "ocsp-ssl.conf"),
                 os.path.join(aug_pre, "ocsp-ssl.conf/IfModule/VirtualHost"),
diff -Nru python-certbot-apache-2.11.0/certbot_apache.egg-info/PKG-INFO 
python-certbot-apache-4.0.0/certbot_apache.egg-info/PKG-INFO
--- python-certbot-apache-2.11.0/certbot_apache.egg-info/PKG-INFO       
2024-06-05 17:34:05.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache.egg-info/PKG-INFO        
2025-04-07 18:03:35.000000000 -0400
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: certbot-apache
-Version: 2.11.0
+Version: 4.0.0
 Summary: Apache plugin for Certbot
 Home-page: https://github.com/certbot/certbot
 Author: Certbot Project
@@ -13,25 +13,33 @@
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Classifier: Topic :: System :: Installation/Setup
 Classifier: Topic :: System :: Networking
 Classifier: Topic :: System :: Systems Administration
 Classifier: Topic :: Utilities
-Requires-Python: >=3.8
+Requires-Python: >=3.9
 License-File: LICENSE.txt
-Requires-Dist: acme>=2.11.0
-Requires-Dist: certbot>=2.11.0
-Requires-Dist: importlib_resources>=1.3.1; python_version < "3.9"
+Requires-Dist: acme>=4.0.0
+Requires-Dist: certbot>=4.0.0
 Requires-Dist: python-augeas
-Requires-Dist: setuptools>=41.6.0
 Provides-Extra: dev
 Requires-Dist: apacheconfig>=0.3.2; extra == "dev"
 Provides-Extra: test
 Requires-Dist: pytest; extra == "test"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
diff -Nru python-certbot-apache-2.11.0/certbot_apache.egg-info/requires.txt 
python-certbot-apache-4.0.0/certbot_apache.egg-info/requires.txt
--- python-certbot-apache-2.11.0/certbot_apache.egg-info/requires.txt   
2024-06-05 17:34:05.000000000 -0400
+++ python-certbot-apache-4.0.0/certbot_apache.egg-info/requires.txt    
2025-04-07 18:03:35.000000000 -0400
@@ -1,10 +1,6 @@
-acme>=2.11.0
-certbot>=2.11.0
+acme>=4.0.0
+certbot>=4.0.0
 python-augeas
-setuptools>=41.6.0
-
-[:python_version < "3.9"]
-importlib_resources>=1.3.1
 
 [dev]
 apacheconfig>=0.3.2
diff -Nru python-certbot-apache-2.11.0/debian/changelog 
python-certbot-apache-4.0.0/debian/changelog
--- python-certbot-apache-2.11.0/debian/changelog       2024-09-18 
15:37:09.000000000 -0400
+++ python-certbot-apache-4.0.0/debian/changelog        2025-05-25 
11:33:48.000000000 -0400
@@ -1,3 +1,16 @@
+python-certbot-apache (4.0.0-2) unstable; urgency=medium
+
+  * autopkgtests: drop manual IP flag no longer used
+
+ -- Harlan Lieberman-Berg <hlieber...@debian.org>  Sun, 25 May 2025 11:33:48 
-0400
+
+python-certbot-apache (4.0.0-1) unstable; urgency=medium
+
+  * d/watch: newer packages use an underscore
+  * New upstream version 4.0.0 (Closes: #1106465)
+
+ -- Harlan Lieberman-Berg <hlieber...@debian.org>  Sat, 24 May 2025 17:15:10 
-0400
+
 python-certbot-apache (2.11.0-1) unstable; urgency=medium
 
   * New upstream version 2.11.0
diff -Nru python-certbot-apache-2.11.0/debian/control 
python-certbot-apache-4.0.0/debian/control
--- python-certbot-apache-2.11.0/debian/control 2024-09-18 15:36:52.000000000 
-0400
+++ python-certbot-apache-4.0.0/debian/control  2025-05-24 16:07:07.000000000 
-0400
@@ -6,9 +6,9 @@
 Build-Depends: debhelper-compat (= 13),
                dh-python,
                python3,
-               python3-acme-abi-2 (>= 2.1),
+               python3-acme-abi-4 (>= 4.0~),
                python3-augeas,
-               python3-certbot-abi-2 (>= 2.1),
+               python3-certbot-abi-4 (>= 4.0~),
                python3-configargparse,
                python3-openssl,
                python3-parsedatetime,
@@ -27,7 +27,7 @@
 Architecture: all
 Depends: apache2,
          certbot,
-         python3-certbot-abi-2 (>= ${Abi-major-minor-version}),
+         python3-certbot-abi-4 (>= ${Abi-major-minor-version}),
          ${misc:Depends},
          ${python3:Depends}
 Suggests: python-certbot-apache-doc
diff -Nru python-certbot-apache-2.11.0/debian/tests/apache 
python-certbot-apache-4.0.0/debian/tests/apache
--- python-certbot-apache-2.11.0/debian/tests/apache    2020-07-24 
01:10:39.000000000 -0400
+++ python-certbot-apache-4.0.0/debian/tests/apache     2025-05-25 
11:33:32.000000000 -0400
@@ -48,7 +48,6 @@
     --no-random-sleep-on-renew \
     --server https://localhost:14000/dir \
     --no-verify-ssl \
-    --manual-public-ip-logging-ok \
     --non-interactive \
     --no-redirect \
     --agree-tos \
diff -Nru python-certbot-apache-2.11.0/debian/watch 
python-certbot-apache-4.0.0/debian/watch
--- python-certbot-apache-2.11.0/debian/watch   2024-09-07 01:16:04.000000000 
-0400
+++ python-certbot-apache-4.0.0/debian/watch    2025-05-24 16:01:13.000000000 
-0400
@@ -1,3 +1,3 @@
 version=4
 opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
-https://pypi.debian.net/certbot-apache/certbot-apache-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
+https://pypi.debian.net/certbot-apache/certbot_apache-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
diff -Nru python-certbot-apache-2.11.0/PKG-INFO 
python-certbot-apache-4.0.0/PKG-INFO
--- python-certbot-apache-2.11.0/PKG-INFO       2024-06-05 17:34:05.165817000 
-0400
+++ python-certbot-apache-4.0.0/PKG-INFO        2025-04-07 18:03:35.462543700 
-0400
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: certbot-apache
-Version: 2.11.0
+Version: 4.0.0
 Summary: Apache plugin for Certbot
 Home-page: https://github.com/certbot/certbot
 Author: Certbot Project
@@ -13,25 +13,33 @@
 Classifier: Operating System :: POSIX :: Linux
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Classifier: Topic :: System :: Installation/Setup
 Classifier: Topic :: System :: Networking
 Classifier: Topic :: System :: Systems Administration
 Classifier: Topic :: Utilities
-Requires-Python: >=3.8
+Requires-Python: >=3.9
 License-File: LICENSE.txt
-Requires-Dist: acme>=2.11.0
-Requires-Dist: certbot>=2.11.0
-Requires-Dist: importlib_resources>=1.3.1; python_version < "3.9"
+Requires-Dist: acme>=4.0.0
+Requires-Dist: certbot>=4.0.0
 Requires-Dist: python-augeas
-Requires-Dist: setuptools>=41.6.0
 Provides-Extra: dev
 Requires-Dist: apacheconfig>=0.3.2; extra == "dev"
 Provides-Extra: test
 Requires-Dist: pytest; extra == "test"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
diff -Nru python-certbot-apache-2.11.0/setup.py 
python-certbot-apache-4.0.0/setup.py
--- python-certbot-apache-2.11.0/setup.py       2024-06-05 17:34:03.000000000 
-0400
+++ python-certbot-apache-4.0.0/setup.py        2025-04-07 18:03:33.000000000 
-0400
@@ -1,7 +1,7 @@
 from setuptools import find_packages
 from setuptools import setup
 
-version = '2.11.0'
+version = '4.0.0'
 
 install_requires = [
     # We specify the minimum acme and certbot version as the current plugin
@@ -9,9 +9,7 @@
     # 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',
-    'setuptools>=41.6.0',
 ]
 
 dev_extras = [
@@ -30,7 +28,7 @@
     author="Certbot Project",
     author_email='certbot-...@eff.org',
     license='Apache License 2.0',
-    python_requires='>=3.8',
+    python_requires='>=3.9',
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Plugins',
@@ -39,11 +37,11 @@
         'Operating System :: POSIX :: Linux',
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
         'Programming Language :: Python :: 3.11',
         'Programming Language :: Python :: 3.12',
+        'Programming Language :: Python :: 3.13',
         'Topic :: Internet :: WWW/HTTP',
         'Topic :: Security',
         'Topic :: System :: Installation/Setup',

Reply via email to