Read README.md for project description.
- Max line width: 150
- The oldest supported Python version is 3.9. Keep implementation and tests
compatible with Python 3.9 unless newer-version behavior is explicitly
guarded or skipped; for example, do not use APIs introduced in Python 3.10+
or 3.11+ such as
asyncio.timeout. - Run
ruff checkafter making changes. The configured line length is inpyproject.toml. Do not use plainflake8for line-length validation unless it is explicitly configured with the same 150-column limit. - Keep
api_<api_name>.pyfiles structurally close to the correspondingasyncioimplementation so upstream changes remain easy to merge. Move genuinely common code toapi_utils.pywhen useful, but keep changes limited to what is necessary: no added typing, no broad refactoring, and no function renaming. Minor local renames are fine when they adapt copied code to aiofastnet conventions, such aslogger->_loggerorself->loop. Fallback code for unsupported event loop implementations, such as proactor loops, is acceptable. - In Cython, side-effect-only
cdefhelpers should returnNoResultwithexcept NoResult.EXC. This avoids aPyErr_Occurred()call on the successful path while making it clear that callers must ignore the return value. Use a meaningful return type instead when callers consume the result. Helpers that are passed to Python APIs as callbacks should instead be untypedcpdeffunctions so Cython provides the normal Python-callable wrapper. - Add a concise comment for compatibility checks or defensive-looking logic
whose necessity is not apparent from the code. Explain the concrete platform,
runtime, or implementation behavior being handled, especially when using
getattr, feature detection, or seemingly redundant conditions. Do not add speculative fallbacks for unsupported or hypothetical environments.
- In tests and test helpers, do not pass optional API parameters just to be explicit. Only specify them when the test depends on that behavior; otherwise redundant arguments can falsely imply hidden requirements.
- Before adding new test scaffolding, inspect nearby tests and
tests/utils.pyfor existing helpers. Prefer shared helpers such asTestServer,TestClient,AsyncClient,EchoServerProtocol, connection-type fixtures, andexc_queueover local protocol classes, manual endpoint setup, explicit transport closing, or custom exception-handler plumbing. - Use
SocketPairfromtests/utils.pywhen a test needs two connected transport endpoints instead of creating and cleaning up socket pairs manually. Itsudpmode usesAF_UNIX/SOCK_DGRAM, so use it for reliable datagram delivery, readiness, and backpressure tests, not for IP addressing or other UDP-specific semantics. - Keep tests focused on the behavior under test. Add local protocols, manual
create_*calls, andtry/finallycleanup only when the shared helpers would hide or prevent the behavior being asserted. - Do not add synthetic monkeypatch tests for platform fallback branches when CI
already runs the native platform/loop combination. For local comparison
against stdlib implementations, use
NO_AIOFN=1.
- Keep benchmark/example protocols minimal. Before adding a new protocol/helper class, first check whether the existing protocol can be extended with the small callback or branch needed for the new transport.
- Prefer reusing the existing benchmark contract (
write_first_data,closed,requests, etc.) over duplicating lifecycle, warmup, timing, and cleanup logic. - For transport variants in examples, keep endpoint creation differences in the helper layer when possible. Do not duplicate protocol classes just because asyncio uses different stream/datagram protocol callback names.
- When adding a feature to an example or benchmark, inspect nearby example code and recent test helper patterns first, then make the smallest change that follows those patterns.
- When investigating hangs, flaky async behavior, SSL issues, or failing tests,
run the focused pytest command with
--asyncio-debug --log-cli-level DEBUG. Example:pytest -s -v -k 'test_name_or_param' --asyncio-debug --log-cli-level DEBUG. aiofastnet logs OpenSSL calls, socket syscalls, and important transport state transitions at DEBUG level.
Defined in tests/utils.py; keep this list in sync with the fixtures.
tcp: plain TCP transport.unix: Unix-domain socket transport; skipped on Windows.udp: UDP datagram transport.pipe: connected read and write pipe transports; Windows selector loops are unsupported.ssl_mbio: TLS over socket transport using memory BIO.ssl_mbio_fall: same shape asssl_mbio, but forcesSSLEngineFallback.ssl_sbio: TLS over socket transport using socket BIO where available.stls: server uses TLS fromcreate_server(ssl=...); client starts plain TCP and then callsstart_tls().ktls: Linux Kernel TLS path; requires supported Python/OpenSSL/kernel setup.