Skip to content

Commit de998a3

Browse files
committed
Update README to highlight Process-Bound Python Environments
- Add process-bound environments as first key feature - Add dedicated section with isolation examples and gen_server pattern - Update documentation links (add process-bound-envs.md, remove deprecated web-frameworks.md)
1 parent c022af6 commit de998a3

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

README.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ schedulers.
2121
- **BEAM processes** - Fan out work across lightweight Erlang processes
2222

2323
Key features:
24+
- **Process-bound environments** - Each Erlang process gets isolated Python state, enabling OTP-supervised Python actors
2425
- **Async/await** - Call Python async functions, gather results, stream from async generators
2526
- **Dirty NIF execution** - Python runs on dirty schedulers, never blocking the BEAM
2627
- **Elixir support** - Works seamlessly from Elixir via the `:py` module
@@ -244,6 +245,52 @@ py:state_clear().
244245

245246
This is backed by ETS with `{write_concurrency, true}`, so counters are atomic and fast.
246247

248+
## Process-Bound Python Environments
249+
250+
Each Erlang process gets its own isolated Python namespace. Variables, imports, and objects defined in one process are invisible to others, even when using the same interpreter.
251+
252+
```erlang
253+
%% Process A defines state
254+
spawn(fun() ->
255+
Ctx = py:context(1),
256+
ok = py:exec(Ctx, <<"counter = 0">>),
257+
{ok, 0} = py:eval(Ctx, <<"counter">>)
258+
end).
259+
260+
%% Process B - same context, but isolated namespace
261+
spawn(fun() ->
262+
Ctx = py:context(1),
263+
%% 'counter' is undefined here - different process
264+
{error, _} = py:eval(Ctx, <<"counter">>)
265+
end).
266+
```
267+
268+
This enables OTP-style patterns for Python:
269+
270+
```erlang
271+
-module(py_counter).
272+
-behaviour(gen_server).
273+
274+
init([]) ->
275+
Ctx = py:context(),
276+
ok = py:exec(Ctx, <<"
277+
class Counter:
278+
def __init__(self): self.value = 0
279+
def incr(self): self.value += 1; return self.value
280+
281+
counter = Counter()
282+
">>),
283+
{ok, #{ctx => Ctx}}.
284+
285+
handle_call(incr, _From, #{ctx := Ctx} = State) ->
286+
{ok, Value} = py:eval(Ctx, <<"counter.incr()">>),
287+
{reply, Value, State}.
288+
```
289+
290+
Resetting Python state is simple: terminate the process. Supervisors can restart it with a fresh environment. No need for manual cleanup.
291+
292+
See [Process-Bound Environments](docs/process-bound-envs.md) for patterns like ML pipelines, stateful actors, and supervision strategies.
293+
247294
## Async/Await Support
248295

249296
Call Python async functions without blocking:
@@ -572,6 +619,7 @@ py:execution_mode(). %% => free_threaded | subinterp | multi_executor
572619
## Documentation
573620

574621
- [Getting Started](docs/getting-started.md)
622+
- [Process-Bound Environments](docs/process-bound-envs.md) - Isolated Python state per Erlang process
575623
- [AI Integration Guide](docs/ai-integration.md)
576624
- [Type Conversion](docs/type-conversion.md)
577625
- [Context Affinity](docs/context-affinity.md)
@@ -582,7 +630,6 @@ py:execution_mode(). %% => free_threaded | subinterp | multi_executor
582630
- [Asyncio Event Loop](docs/asyncio.md) - Erlang-native asyncio with TCP/UDP support
583631
- [Reactor](docs/reactor.md) - FD-based protocol handling
584632
- [Security](docs/security.md) - Sandbox and blocked operations
585-
- [Web Frameworks](docs/web-frameworks.md) - ASGI/WSGI integration
586633
- [Changelog](https://github.com/benoitc/erlang-python/releases)
587634

588635
## License

0 commit comments

Comments
 (0)