Skip to content

Commit 8d31bce

Browse files
committed
Add exec and eval functions to py_event_loop_pool
Routes to the appropriate loop based on process affinity (PID hash). Falls back to default event loop if pool is not available.
1 parent 89c4dab commit 8d31bce

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

src/py_event_loop_pool.erl

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@
4040
create_task/3, create_task/4,
4141
run/3, run/4,
4242
spawn_task/3, spawn_task/4,
43-
await/1, await/2
43+
await/1, await/2,
44+
%% Per-process namespace API
45+
exec/1, exec/2,
46+
eval/1, eval/2
4447
]).
4548

4649
%% Legacy API
@@ -272,6 +275,63 @@ await(Ref, Timeout) ->
272275
{error, timeout}
273276
end.
274277

278+
%%% ============================================================================
279+
%%% Per-Process Namespace API
280+
%%% ============================================================================
281+
282+
%% @doc Execute Python code in the calling process's event loop namespace.
283+
%%
284+
%% Each Erlang process gets an isolated Python namespace for its assigned
285+
%% event loop. Functions defined via exec/1 can be called via create_task/3
286+
%% with the `__main__' module.
287+
%%
288+
%% Example:
289+
%% <pre>
290+
%% ok = py_event_loop_pool:exec(&lt;&lt;"
291+
%% async def my_async_func(x):
292+
%% return x * 2
293+
%% "&gt;&gt;),
294+
%% Ref = py_event_loop_pool:create_task('__main__', my_async_func, [21]),
295+
%% {ok, 42} = py_event_loop_pool:await(Ref)
296+
%% </pre>
297+
-spec exec(Code :: binary() | iolist()) -> ok | {error, term()}.
298+
exec(Code) ->
299+
case get_loop() of
300+
{ok, LoopRef} ->
301+
exec(LoopRef, Code);
302+
{error, not_available} ->
303+
%% Fallback to default event loop
304+
py_event_loop:exec(Code)
305+
end.
306+
307+
-spec exec(LoopRef :: reference(), Code :: binary() | iolist()) -> ok | {error, term()}.
308+
exec(LoopRef, Code) ->
309+
py_nif:event_loop_exec(LoopRef, Code).
310+
311+
%% @doc Evaluate a Python expression in the calling process's namespace.
312+
%%
313+
%% Returns the result of evaluating the expression.
314+
%%
315+
%% Example:
316+
%% <pre>
317+
%% ok = py_event_loop_pool:exec(&lt;&lt;"x = 42"&gt;&gt;),
318+
%% {ok, 42} = py_event_loop_pool:eval(&lt;&lt;"x"&gt;&gt;),
319+
%% {ok, 84} = py_event_loop_pool:eval(&lt;&lt;"x * 2"&gt;&gt;)
320+
%% </pre>
321+
-spec eval(Expr :: binary() | iolist()) -> {ok, term()} | {error, term()}.
322+
eval(Expr) ->
323+
case get_loop() of
324+
{ok, LoopRef} ->
325+
eval(LoopRef, Expr);
326+
{error, not_available} ->
327+
%% Fallback to default event loop
328+
py_event_loop:eval(Expr)
329+
end.
330+
331+
-spec eval(LoopRef :: reference(), Expr :: binary() | iolist()) -> {ok, term()} | {error, term()}.
332+
eval(LoopRef, Expr) ->
333+
py_nif:event_loop_eval(LoopRef, Expr).
334+
275335
%%% ============================================================================
276336
%%% Legacy API
277337
%%% ============================================================================

0 commit comments

Comments
 (0)