|
1 | 1 | # Changelog |
2 | 2 |
|
3 | | -## 2.2.0 (unreleased) |
| 3 | +## 2.2.0 (2026-03-24) |
4 | 4 |
|
5 | 5 | ### Added |
6 | 6 |
|
| 7 | +- **OWN_GIL Mode** - True parallel Python execution with Python 3.14+ subinterpreters |
| 8 | + - Each subinterpreter runs with its own GIL (`Py_GIL_OWN`) in a dedicated thread |
| 9 | + - Full isolation between interpreters (separate namespaces, modules, state) |
| 10 | + - `py_context:start_link(N, owngil)` to create OWN_GIL contexts |
| 11 | + - Enables true parallelism for CPU-bound Python workloads |
| 12 | + - See [OWN_GIL Internals](docs/owngil_internals.md) for architecture details |
| 13 | + |
| 14 | +- **Process-Bound Python Environments** - Per-Erlang-process Python namespaces |
| 15 | + - Each Erlang process gets isolated Python globals/locals |
| 16 | + - State persists across calls within the same process |
| 17 | + - Automatic cleanup when Erlang process terminates |
| 18 | + - See [Process-Bound Environments](docs/process-bound-envs.md) for details |
| 19 | + |
| 20 | +- **Event Loop Pool** - Process affinity for parallel async execution |
| 21 | + - `py_event_loop_pool` distributes async tasks across multiple event loops |
| 22 | + - Scheduler-affinity routing for cache-friendly execution |
| 23 | + - Supports worker, subinterp, and owngil modes |
| 24 | + |
| 25 | +- **ByteChannel API** - Raw byte streaming without term serialization |
| 26 | + - `py_byte_channel:new/0,1` - Create byte channels |
| 27 | + - `py_byte_channel:send/2` - Send raw bytes |
| 28 | + - `py_byte_channel:recv/1,2` - Receive bytes |
| 29 | + - Python `ByteChannel` class with sync/async iteration |
| 30 | + - Ideal for HTTP bodies, file streaming, binary protocols |
| 31 | + |
| 32 | +- **PyBuffer API** - Zero-copy buffer for WSGI input streams |
| 33 | + - `py_buffer:new/0,1` - Create buffers with optional max size |
| 34 | + - `py_buffer:write/2` - Write data to buffer |
| 35 | + - Python `PyBuffer` class with file-like interface (`read`, `readline`, `readlines`) |
| 36 | + - Non-blocking reads for async I/O patterns |
| 37 | + - See [Buffer API](docs/buffer.md) for details |
| 38 | + |
7 | 39 | - **True streaming API** - New `py:stream_start/3,4` and `py:stream_cancel/1` functions |
8 | 40 | for event-driven streaming from Python generators. Unlike `py:stream/3,4` which |
9 | 41 | collects all values at once, `stream_start` sends `{py_stream, Ref, {data, Value}}` |
10 | 42 | messages as values are yielded. Supports both sync and async generators. Useful for |
11 | 43 | LLM token streaming, real-time data feeds, and processing large sequences incrementally. |
12 | 44 |
|
| 45 | +- **`erlang.whereis(name)`** - Lookup registered Erlang process PIDs from Python |
| 46 | + - Returns `erlang.Pid` object or `None` if not registered |
| 47 | + - Enables Python code to discover and message named processes |
| 48 | + |
| 49 | +- **`erlang.schedule_inline(callback)`** - Inline continuation scheduling |
| 50 | + - Release dirty scheduler and continue with callback in same context |
| 51 | + - Preserves globals/locals across the continuation |
| 52 | + - Useful for cooperative long-running tasks |
| 53 | + |
| 54 | +- **`py:spawn_call/3,4,5`** - Fire-and-forget with result delivery |
| 55 | + - Executes Python call asynchronously |
| 56 | + - Sends `{py_result, Ref, Result}` to caller when complete |
| 57 | + - Non-blocking alternative to `py:call` for async patterns |
| 58 | + |
| 59 | +- **Explicit bytes conversion** - `{bytes, Binary}` tuple for round-trip safety |
| 60 | + - Erlang binaries convert to Python `str` by default |
| 61 | + - Use `{bytes, Binary}` to force Python `bytes` type |
| 62 | + - Ensures correct handling for binary protocols |
| 63 | + |
| 64 | +- **Import caching API** - Lazy module import with caching |
| 65 | + - `py:import/1,2` - Import and cache modules |
| 66 | + - `py:add_import/1,2` - Register imports applied to all contexts |
| 67 | + - `py:add_path/1` - Add to sys.path across all contexts |
| 68 | + - Per-interpreter caching with generation tracking |
| 69 | + |
| 70 | +- **Per-interpreter preload code** - Execute code in new interpreters |
| 71 | + - Configure via `{erlang_python, [{preload_code, <<"import mylib">>}]}` |
| 72 | + - Code runs with inherited globals from main interpreter |
| 73 | + - Useful for initializing common imports/state |
| 74 | + |
13 | 75 | ### Fixed |
14 | 76 |
|
15 | 77 | - **Channel notification for create_task** - Fixed async channel receive hanging when using |
|
51 | 113 |
|
52 | 114 | ### Changed |
53 | 115 |
|
| 116 | +- **`py:cast` is now fire-and-forget** - `py:cast/3,4,5` no longer returns a reference. |
| 117 | + For async calls with result delivery, use the new `py:spawn_call/3,4,5` instead. |
| 118 | + |
| 119 | +- **OWN_GIL requires Python 3.14+** - The OWN_GIL subinterpreter mode requires Python 3.14 |
| 120 | + or later due to C extension compatibility issues in earlier versions. Use `worker` or |
| 121 | + `subinterp` modes for Python 3.12-3.13. |
| 122 | + |
54 | 123 | - **Removed auto-started io pool** - The io pool is no longer started automatically at |
55 | 124 | application startup to reduce memory usage. Users who need a dedicated I/O pool can |
56 | 125 | create one manually via `py_context_router:start_pool(io, 10, worker)`. The configuration |
|
69 | 138 |
|
70 | 139 | ### Performance |
71 | 140 |
|
| 141 | +- **Direct NIF channel operations** - Channel send/receive bypass `erlang.call()` overhead |
| 142 | + for up to 1760x speedup in raw throughput benchmarks |
| 143 | + |
72 | 144 | - **nif_process_ready_tasks optimization** - ~15% improvement in async task processing |
73 | 145 | - Replace `asyncio.iscoroutine()` with `PyCoro_CheckExact` C API |
74 | 146 | - Use stack buffers for module/func strings |
|
0 commit comments