diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index fb6b5f3b411b22..acb31f38251f74 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1127,29 +1127,36 @@ def emit(self, record): msg['Subject'] = self.getSubject(record) msg['Date'] = email.utils.localtime() msg.set_content(self.format(record)) - if self.username: - if self.secure is not None: - import ssl - - try: - keyfile = self.secure[0] - except IndexError: - keyfile = None - - try: - certfile = self.secure[1] - except IndexError: - certfile = None - - context = ssl._create_stdlib_context( - certfile=certfile, keyfile=keyfile - ) - smtp.ehlo() - smtp.starttls(context=context) - smtp.ehlo() - smtp.login(self.username, self.password) - smtp.send_message(msg) - smtp.quit() + try: + if self.username: + if self.secure is not None: + import ssl + + try: + keyfile = self.secure[0] + except IndexError: + keyfile = None + + try: + certfile = self.secure[1] + except IndexError: + certfile = None + + context = ssl._create_stdlib_context( + certfile=certfile, keyfile=keyfile + ) + smtp.ehlo() + smtp.starttls(context=context) + smtp.ehlo() + smtp.login(self.username, self.password) + smtp.send_message(msg) + smtp.quit() + except Exception: + try: + smtp.close() + except Exception: + pass + raise except Exception: self.handleError(record) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 7cd0df3ea0b62d..2e3f8bdcad78a1 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1171,6 +1171,25 @@ def test_basic(self): self.assertEndsWith(data, '\n\nHello \u2713') h.close() + def test_connection_closed_on_send_failure(self): + handler = logging.handlers.SMTPHandler( + ("localhost", 25), + "me@example.com", + "you@example.com", + "subject", + timeout=self.TIMEOUT, + ) + record = logging.makeLogRecord({"msg": "hello"}) + + smtp = unittest.mock.Mock() + smtp.send_message.side_effect = OSError("send failed") + + with unittest.mock.patch("smtplib.SMTP", return_value=smtp): + with support.captured_stderr(): + handler.emit(record) + + smtp.close.assert_called_once() + def process_message(self, *args): self.messages.append(args) self.handled.set() diff --git a/Misc/NEWS.d/next/Library/2026-08-17-10-12-27.gh-issue-155946.NCtsd4.rst b/Misc/NEWS.d/next/Library/2026-08-17-10-12-27.gh-issue-155946.NCtsd4.rst new file mode 100644 index 00000000000000..73e89f2638d2d7 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-17-10-12-27.gh-issue-155946.NCtsd4.rst @@ -0,0 +1,2 @@ +:mod:`logging`: ensure that :class:`logging.handlesr.SMTPHandler` +correctly releases resources on SMTP connection failures.