logging: `SMTPHandler.emit()` leaks the SMTP connection when sending fails
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 77.2k
- Forks
- 36k
- PR merge metrics
- PR metrics pending
Description
Bug report
Bug description:
SMTPHandler.emit() only closes its SMTP connection on the success path: smtp.quit() is the last statement of the try block (Lib/logging/handlers.py#L1109-L1154).
If starttls(), login() or send_message() raises, control jumps to except Exception: self.handleError(record) and the connection is never closed — cleanup is left to the garbage collector.
Reproducer
import gc, logging, logging.handlers, socket, threading, time
logging.raiseExceptions = False
open_conns = []
def handle(conn):
conn.sendall(b"220 fake ESMTP\r\n")
for line in conn.makefile("rb"):
cmd = line.strip().upper()
if cmd.startswith(b"EHLO"):
conn.sendall(b"250-fake\r\n250 AUTH PLAIN LOGIN\r\n")
elif cmd.startswith(b"AUTH"):
conn.sendall(b"535 authentication failed\r\n")
else:
conn.sendall(b"250 ok\r\n")
open_conns.remove(conn) # reached when the client closes the connection
conn.close()
def serve(listener):
while True:
conn, _ = listener.accept()
open_conns.append(conn)
threading.Thread(target=handle, args=(conn,), daemon=True).start()
listener = socket.create_server(("127.0.0.1", 0))
threading.Thread(target=serve, args=(listener,), daemon=True).start()
h = logging.handlers.SMTPHandler(("127.0.0.1", listener.getsockname()[1]),
"me@example.com", "you@example.com", "subject",
credentials=("user", "wrong-password"))
for i in range(5):
h.emit(logging.makeLogRecord({"msg": "hello"})) # SMTPAuthenticationError
time.sleep(0.5)
print("open connections after 5 failed emits:", len(open_conns))
gc.collect(); time.sleep(0.5)
print("after gc.collect():", len(open_conns))
Output on main:
open connections after 5 failed emits: 5
after gc.collect(): 0
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs
- gh-155950
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in Lib/logging/handlers.py at SMTPHandler.emit() and run the provided authentication-failure reproducer. Done means failed starttls(), login(), or send_message() paths no longer leave SMTP connections open, with behavior checked against the reproducer; linked PR gh-155950 indicates work is already underway.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100