Skip to content

Latest commit

 

History

History
220 lines (155 loc) · 4.88 KB

File metadata and controls

220 lines (155 loc) · 4.88 KB

Getting Started

This guide walks you through using erlang_python to execute Python code from Erlang.

Installation

Add to your rebar.config:

{deps, [
    {erlang_python, {git, "https://github.com/benoitc/erlang-python.git", {tag, "v1.0.0"}}}
]}.

Starting the Application

1> application:ensure_all_started(erlang_python).
{ok, [erlang_python]}

The application starts a pool of Python worker processes that handle requests.

Basic Usage

Calling Python Functions

%% Call math.sqrt(16)
{ok, 4.0} = py:call(math, sqrt, [16]).

%% Call json.dumps with keyword arguments
{ok, Json} = py:call(json, dumps, [#{name => <<"Alice">>}], #{indent => 2}).

Evaluating Expressions

%% Simple arithmetic
{ok, 42} = py:eval(<<"21 * 2">>).

%% Using Python built-ins
{ok, 45} = py:eval(<<"sum(range(10))">>).

%% With local variables
{ok, 100} = py:eval(<<"x * y">>, #{x => 10, y => 10}).

Executing Statements

Use py:exec/1 to execute Python statements:

ok = py:exec(<<"
import random

def roll_dice(sides=6):
    return random.randint(1, sides)
">>).

Note: Definitions made with exec are local to the worker that executes them. Subsequent calls may go to different workers.

Working with Timeouts

All operations support optional timeouts:

%% 5 second timeout
{ok, Result} = py:call(mymodule, slow_func, [], #{}, 5000).

%% Timeout error
{error, timeout} = py:eval(<<"sum(range(10**9))">>, #{}, 100).

Async Calls

For non-blocking operations:

%% Start async call
Ref = py:call_async(math, factorial, [1000]).

%% Do other work...

%% Wait for result
{ok, HugeNumber} = py:await(Ref).

Streaming from Generators

Python generators can be streamed efficiently:

%% Stream a generator expression
{ok, [0,1,4,9,16]} = py:stream_eval(<<"(x**2 for x in range(5))">>).

%% Stream from a generator function (if defined)
{ok, Chunks} = py:stream(mymodule, generate_data, [arg1, arg2]).

Type Conversions

Values are automatically converted between Erlang and Python:

%% Numbers
{ok, 42} = py:eval(<<"42">>).           %% int -> integer
{ok, 3.14} = py:eval(<<"3.14">>).       %% float -> float

%% Strings
{ok, <<"hello">>} = py:eval(<<"'hello'">>).  %% str -> binary

%% Collections
{ok, [1,2,3]} = py:eval(<<"[1,2,3]">>).      %% list -> list
{ok, {1,2,3}} = py:eval(<<"(1,2,3)">>).      %% tuple -> tuple
{ok, #{<<"a">> := 1}} = py:eval(<<"{'a': 1}">>).  %% dict -> map

%% Booleans and None
{ok, true} = py:eval(<<"True">>).
{ok, false} = py:eval(<<"False">>).
{ok, none} = py:eval(<<"None">>).

Execution Mode and Scalability

Check the current execution mode:

%% See how Python is being executed
py:execution_mode().
%% => free_threaded | subinterp | multi_executor

%% Check rate limiting status
py_semaphore:max_concurrent().  %% Maximum concurrent calls
py_semaphore:current().         %% Currently executing

See Scalability for details on execution modes and performance tuning.

Using from Elixir

erlang_python works seamlessly with Elixir. The :py module can be called directly:

# Start the application
{:ok, _} = Application.ensure_all_started(:erlang_python)

# Call Python functions
{:ok, 4.0} = :py.call(:math, :sqrt, [16])

# Evaluate expressions
{:ok, result} = :py.eval("2 + 2")

# With variables
{:ok, 100} = :py.eval("x * y", %{x: 10, y: 10})

# Call with keyword arguments
{:ok, json} = :py.call(:json, :dumps, [%{name: "Elixir"}], %{indent: 2})

Register Elixir Functions for Python

# Register an Elixir function
:py.register_function(:factorial, fn [n] ->
  Enum.reduce(1..n, 1, &*/2)
end)

# Call from Python
{:ok, 3628800} = :py.eval("__import__('erlang').call('factorial', 10)")

# Cleanup
:py.unregister_function(:factorial)

Parallel Processing with BEAM

# Register parallel map using BEAM processes
:py.register_function(:parallel_map, fn [func_name, items] ->
  parent = self()

  refs = Enum.map(items, fn item ->
    ref = make_ref()
    spawn(fn ->
      result = apply_function(func_name, item)
      send(parent, {ref, result})
    end)
    ref
  end)

  Enum.map(refs, fn ref ->
    receive do
      {^ref, result} -> result
    after
      5000 -> {:error, :timeout}
    end
  end)
end)

Running the Elixir Example

A complete working example is available:

elixir --erl "-pa _build/default/lib/erlang_python/ebin" examples/elixir_example.exs

This demonstrates basic calls, data conversion, callbacks, parallel processing (10x speedup), and AI integration.

Next Steps