All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.4.1 - 2026-02-25
- ASGI Scope Method Bug: Fixed issue where HTTP method was not treated as a dynamic field in scope caching, causing incorrect method values when the same path was accessed with different HTTP methods
- Documentation Links: Fixed broken links in docs using relative paths with
.mdextension - now use absolute paths without extension for website compatibility - Test Module Collision: Renamed
examples/hello_asgi/app.pytohello_asgi_app.pyto avoid Python module cache conflicts when running full test suite
- Updated
erlang_pythonto 1.8.1 (fixes ASGI scope method caching)
1.4.0 - 2026-02-25
- Erlang-Asyncio Integration: Native Erlang timer support for async operations
- Auto-detection of
asyncio.sleep()in ASGI fast path - Uses Erlang's native timer via
_erlang_sleepfor improved async performance
- Auto-detection of
- 6-Stage ASGI/WSGI Performance Optimizations:
- Per-app execution mode caching to skip fast path overhead for apps requiring event loop
- Persistent event loop uses
threading.Eventinstead of busy-spin polling - Dev-only module eviction via
HORNBEAM_DEV_RELOADenvironment variable - Event-driven WebSocket wakeups using
asyncio.Event+asyncio.wait - Request-local queues via
contextvarsto prevent cross-request bleed under concurrent load - WSGI environ optimization with cached values and shared instances
- Non-blocking
stream_async()yields control between chunks
- Simple ASGI: ~66k req/s
- High concurrency (500 connections): ~71k req/s
- Async sleep (1ms): ~8.6k req/s
- Concurrent tasks: ~6.2k req/s
- Updated
erlang_pythonto 1.8.0
1.3.2 - 2026-02-23
- Channel handlers swallowing
SuspensionRequiredexceptions (PR #5) - Broken links in docs index - use absolute paths without .md extension
- Request limits implementation
- Updated
erlang_pythonto 1.7.1- 1.7.x: Shared router architecture for event loops, isolated event loops support
- 1.6.1: ASGI headers now correctly use bytes instead of str (spec compliance fix)
- 1.6.0: Python logging integration (
py:configure_logging), distributed tracing (erlang.Span), type conversion optimizations
1.3.1 - 2026-02-18
-
SSL/TLS Support: Full SSL/TLS configuration now working
ssloption to enable TLScertfileandkeyfilefor certificate configurationcacertfilefor CA certificate chain- Validation ensures cert/key are provided when SSL enabled
-
WebSocket Compression:
websocket_compressoption to enable per-message deflate -
HTTP Lifecycle Hooks: New
hornbeam_http_hooksmoduleon_requesthook for request modification/loggingon_responsehook for response modificationon_errorhook for custom error handling- Exception-safe hook execution
- Fixed
venvconfiguration option not activating virtual environment
- Removed
python_homefrom documentation (cannot be controlled by hornbeam)
1.3.0 - 2026-02-18
-
ASGI/WSGI NIF Optimizations: Direct C-level marshalling for ~2x throughput improvement
py_asgi:run/5NIF with interned scope keys and cached constantspy_wsgi:run/4NIF with interned environ keys and cached constants- Per-interpreter state for sub-interpreter and free-threading support
- Response pooling for reduced memory allocation
- ASGI: 27k → 65k req/s (~2.4x improvement)
- WSGI: 30k → 65k req/s (~2x improvement)
-
Context Affinity Option:
context_affinityconfiguration option for apps requiring module-level state sharing with lifespan context
- ASGI handler now uses
py_asgi:run/5optimized NIF path by default - WSGI handler now uses
py_wsgi:run/4optimized NIF path by default - Fallback to
py:ctx_callwhencontext_affinityis enabled
- Updated
erlang_pythonto 1.5.0 (adds py_asgi and py_wsgi NIF modules)
1.2.0 - 2026-02-18
-
Channels & Presence: Real-time multiplexed channels with presence tracking
hornbeam_channelgen_server for channel lifecycle managementhornbeam_channel_registryfor pattern-based channel routinghornbeam_presencefor CRDT-backed distributed presence- Python decorator API (
@channel.on_join,@channel.on("event")) - Broadcasting (
broadcast,broadcast_from) - Presence tracking (
Presence.track,Presence.list) - JavaScript client with Socket, Channel, and Presence classes
-
Documentation
- Channels & Presence guide
- Channels Chat example
-
Erlang Event Loop Integration: Replaced uvloop with erlang_loop for native Erlang scheduler integration
- ASGI requests now use the erlang event loop policy from erlang_python
- Better integration with Erlang's cooperative scheduling
-
ASGI Runner Optimizations:
- Added
__slots__to ASGIResponse class for reduced memory allocation - Created
_ReceiveCallableclass to avoid closure creation per request - Cached lifespan state getter at module level (avoids import on every request)
- Optimized type checks using
__class__ isinstead ofisinstance() - Optimized
_run_sync_coroutineto use try/except instead of hasattr
- Added
- Updated
erlang_pythonto 1.4.0 (hex package)
- Fixed dialyzer warnings in channel and websocket modules
1.1.0 - 2026-02-17
- uvloop Integration: Install uvloop as default event loop policy per worker for improved async performance
- ASGI Benchmarks: Added ASGI benchmark suite and optimized request handling
- Moved Benchmarking to bottom of Guides section in documentation
- Fixed syntax highlighting contrast in documentation
1.0.0 - 2026-02-17
-
WSGI Support: Full PEP 3333 compliance for running Python WSGI applications
- Complete environ dict with all required and recommended variables
wsgi.file_wrapperfor efficient file servingwsgi.early_hintsfor 103 Early Hints responseswsgi.errorsrouting to Erlang logging
-
ASGI Support: Full ASGI 3.0 protocol implementation
- HTTP scope with streaming responses
- WebSocket scope with RFC 6455 support
- Lifespan protocol with
hornbeam_lifespangen_server lifespan_timeoutconfiguration for startup/shutdown timeout- Python context affinity for module state persistence
- Informational responses (1xx)
-
HTTP Features (via Cowboy)
- HTTP/1.1 with keep-alive and chunked encoding
- HTTP/2 with multiplexing
- TLS/SSL support
- WebSocket with binary and text frames
-
Erlang Integration
- Shared state via ETS (
hornbeam_statemodule) - Distributed RPC to remote nodes (
hornbeam_distmodule) - Pub/Sub messaging via pg (
hornbeam_pubsubmodule) - Registered Erlang functions callable from Python (
hornbeam_callbacksmodule)
- Shared state via ETS (
-
Hooks System
on_request- Modify requests before handlingon_response- Modify responses before sendingon_error- Custom error handlingon_worker_start/on_worker_exit- Worker lifecycle hooks
-
ML Integration
hornbeam_mlPython module for cached inference- ETS-backed caching with hit/miss statistics
- Support for distributed ML across Erlang cluster
-
Python Modules
hornbeam_erlang- State, RPC, Pub/Sub, and callback APIshornbeam_ml- ML caching helpershornbeam_wsgi_runner- WSGI request handlinghornbeam_asgi_runner- ASGI request handlinghornbeam_websocket_runner- WebSocket session handlinghornbeam_lifespan_runner- Lifespan protocol handling
-
Configuration
- Server binding, SSL/TLS options
- Worker pool sizing and timeouts
- ASGI lifespan control
- WebSocket timeout and frame size limits
- Python path and virtual environment support
-
Demo Examples
- ML Caching: OTP application with hook-based architecture
- Distributed RPC: 3-node Erlang cluster with Docker Compose
- Real-time Chat: WebSocket with Erlang pub/sub
- Docker support for all demo applications
-
Documentation
- Getting started guide
- WSGI, ASGI, WebSocket guides
- Erlang integration guide
- ML integration guide
- Flask, FastAPI, WebSocket chat examples
- Embedding service and distributed ML examples
- Configuration reference
- Benchmarking guide
-
Website
- https://hornbeam.dev
- Product pages for Hornbeam and Erlang Python
- Integrated documentation with product switcher
- Erlang/OTP 27+
- Python 3.12+ (3.13+ recommended for free-threading)
- Cowboy 2.12.0
- erlang_python 1.3.2