Install & Compatibility
Where this runs
tested against v1.4.6 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.346s · 19.4MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.7s · import 0.310s · 20MB
18MB installed
● package 18MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Controller
✓ from aiosmtpd.controller import Controller
✗ from aiosmtpd import Controller
The Controller class is in the 'controller' submodule.
SMTP
✓ from aiosmtpd.smtp import SMTP
Directly using SMTP is possible but Controller is often preferred for managing the server in a separate thread.
handlers.Sink
✓ from aiosmtpd.handlers import Sink
Commonly used handler for testing, discards all incoming mail.
handlers.Debugging
✓ from aiosmtpd.handlers import Debugging
Default handler for the command-line script; prints incoming mail to stdout.
This quickstart sets up a basic SMTP server on `127.0.0.1:8025` using the `Debugging` handler, which prints all received emails to the console. It leverages `Controller` to run the server in a separate thread, allowing the main program to continue running or wait for interruption. You can connect to this server with any SMTP client (e.g., `smtplib` or `telnet`).
import asyncio
from aiosmtpd.controller import Controller
from aiosmtpd.handlers import Debugging
import os
async def amain():
# Use Debugging handler to print incoming emails to console
handler = Debugging()
# The Controller runs the SMTP server in a separate thread.
# For testing/quickstart, localhost:8025 is common.
controller = Controller(handler, hostname=os.environ.get('SMTP_HOST', '127.0.0.1'), port=int(os.environ.get('SMTP_PORT', 8025)))
print(f"Starting SMTP server on {controller.hostname}:{controller.port}...")
controller.start()
print("SMTP server started. Press Ctrl+C to stop.")
try:
# Keep the main loop running while the controller's thread handles the SMTP server
await asyncio.Event().wait()
except asyncio.CancelledError:
pass
finally:
controller.stop()
print("SMTP server stopped.")
if __name__ == '__main__':
# Run the asyncio event loop
try:
asyncio.run(amain())
except KeyboardInterrupt:
print("Server interrupted by user.")
aiosmtpd --version
Debug
Known issues
breakingThe signature of the `handle_NOOP()` method in custom handlers changed from taking zero arguments to requiring a single argument in version 1.4.x. Custom handlers implementing this method must be updated.fixUpdate `handle_NOOP(self, server)` to accept the `server` argument: `async def handle_NOOP(self, server): ...`
affects: >=1.4.0
gotchaBy default, the SMTP AUTH extension might not be advertised or supported by the server unless a STARTTLS command is issued first, or `auth_require_tls=False` is explicitly passed to the `Controller` or `SMTP` constructor. This can cause authentication failures with clients expecting immediate AUTH support.fixIf clients connect without STARTTLS and require AUTH, initialize `Controller` or `SMTP` with `auth_require_tls=False`: `Controller(handler, ..., auth_require_tls=False)`.
affects: All versions
deprecatedThe `process_message()` method in handler classes was deprecated. Implementations should now use the asynchronous `handle_DATA(self, server, session, envelope)` method for processing incoming mail data.fixMigrate `process_message()` logic to `async def handle_DATA(self, server, session, envelope): ...` ensuring it returns an SMTP response string (e.g., `'250 OK'`).
affects: <1.0a5
deprecatedThe `authentication_handler` parameter for the `SMTP` class constructor was deprecated in favor of `authenticator` and is scheduled for removal in version 2.0.fixReplace `authentication_handler=my_func` with `authenticator=Authenticator(my_func)` (where `Authenticator` is imported from `aiosmtpd.smtp`).
affects: >=1.3.0
gotchaThe default `ready_timeout` for `Controller.start()` to wait for the SMTP server thread to become ready changed from 1 second to 5 seconds. This could impact testing setups or deployments sensitive to startup times.fixAdjust tests or application logic that rely on a specific short startup timeout, or explicitly set `ready_timeout` in the `Controller` constructor: `Controller(handler, ..., ready_timeout=1.0)`.
affects: >=1.4.2
breakingWhile PyPI states `requires_python >=3.8`, the official documentation recommends CPython>=3.9 and PyPy>=3.9. The upcoming version 1.4.7 explicitly drops support for Python 3.8, requiring an upgrade if using newer `aiosmtpd` versions.fixEnsure your environment uses Python 3.9 or newer to guarantee compatibility with current and future `aiosmtpd` releases.
affects: >=1.4.7 (future)
Errors
Common errors & fixes
ModuleNotFoundError: No module named 'aiosmtpd'
The 'aiosmtpd' package is not installed in the Python environment.
fixInstall the package using pip: 'pip install aiosmtpd'.
ModuleNotFoundError: No module named 'public'
An outdated version of 'setuptools' is being used, which lacks support for the 'public' module required by 'aiosmtpd'.
fixUpgrade 'setuptools' to at least version 46.4.0: 'pip install --upgrade setuptools'.
ImportError: No module named 'aiosmtpd'
The 'aiosmtpd' package is not installed in the Python environment.
fixInstall the package using pip: 'pip install aiosmtpd'.
OSError: [Errno 98] Address already in use
This error occurs when the `aiosmtpd` server attempts to bind to a network address (IP and port) that is already being used by another process on your system.
fixEnsure no other process is listening on the desired port. You can either choose a different port for your `aiosmtpd` server or terminate the process currently occupying the port. For development, you can often add `sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)` (though `aiosmtpd`'s `Controller` usually handles this; checking for an actively running process is the primary solution).
Connection refused
This commonly happens when an `aiosmtpd` server is configured to listen only on localhost (e.g., '::1' or '127.0.0.1') by default, but a client attempts to connect from a different IP address, or when the server is not running or a firewall is blocking the connection.
fixWhen initializing the `Controller`, specify `hostname='0.0.0.0'` to listen on all available network interfaces, allowing connections from other machines or Docker containers. Also, verify the server is running and that no firewall is blocking the port.
Upgrade
Version history
1.4.6latest on PyPI · released May 18, 2024
Audit
Dependencies
PythonrequiredOfficially tested on CPython>=3.9 and PyPy>=3.9, though PyPI metadata states >=3.8. Future versions will drop Python 3.8 support.