Current section

Files

Jump to
erlang_python src py_preload.erl
Raw

src/py_preload.erl

%% Copyright 2026 Benoit Chesneau
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%% @doc Python preload code registry.
%%%
%%% Allows users to preload Python code that executes during interpreter
%%% initialization. The resulting globals become the base namespace that
%%% process-local environments inherit from.
%%%
%%% == Usage ==
%%%
%%% ```
%%% %% At application startup
%%% py_preload:set_code(<<"
%%% import json
%%% import os
%%%
%%% def shared_helper(x):
%%% return x * 2
%%%
%%% CONFIG = {'debug': True}
%%% ">>).
%%%
%%% %% Later, any context will have these preloaded
%%% {ok, Ctx} = py_context:new(#{mode => worker}),
%%% {ok, 10} = py:eval(Ctx, <<"shared_helper(5)">>).
%%% '''
%%%
%%% == Storage ==
%%%
%%% Uses `persistent_term' for the preload code. Changes only affect
%%% newly created contexts; existing contexts are not modified.
%%%
%%% @end
-module(py_preload).
-export([
set_code/1,
get_code/0,
clear_code/0,
has_preload/0,
apply_preload/1
]).
-define(PRELOAD_KEY, {py_preload, code}).
%% @doc Set preload code to be executed once per interpreter at init.
%%
%% The code is executed in the interpreter's `__main__' namespace.
%% All defined functions, variables, and imports become available
%% in process-local environments.
%%
%% @param Code Python code as binary or iolist
-spec set_code(binary() | iolist()) -> ok.
set_code(Code) when is_binary(Code); is_list(Code) ->
persistent_term:put(?PRELOAD_KEY, iolist_to_binary(Code)).
%% @doc Get the current preload code.
%%
%% @returns The preload code binary, or `undefined' if not set
-spec get_code() -> binary() | undefined.
get_code() ->
try
persistent_term:get(?PRELOAD_KEY)
catch
error:badarg -> undefined
end.
%% @doc Clear the preload code.
%%
%% New contexts will start with empty globals. Existing contexts
%% are not affected.
-spec clear_code() -> ok.
clear_code() ->
try
persistent_term:erase(?PRELOAD_KEY)
catch
error:badarg -> ok
end,
ok.
%% @doc Check if preload code is configured.
-spec has_preload() -> boolean().
has_preload() ->
get_code() =/= undefined.
%% @doc Apply preload code to a context reference.
%%
%% Called internally by `py_context' during context initialization.
%% Executes the preload code in the context's interpreter.
%%
%% @param Ref NIF context reference
%% @returns `ok' if successful or no preload configured, `{error, Reason}' on failure
-spec apply_preload(reference()) -> ok | {error, term()}.
apply_preload(Ref) ->
case get_code() of
undefined -> ok;
Code -> py_nif:context_exec(Ref, Code)
end.