pip3 install git+https://github.com/skosch/python-erlasticErlastic allows you to serialize/deserialize python objects into erlang binary term.
Basic usage is :
importerlasticpy_struct=erlastic.decode(binary_term)
binary=erlastic.encode(py_struct)The library contains also a function to use python with erlastic in an erlang
port to communicate erlang binary term : port_communication() which return
(mailbox,port). They are both python coroutines (executed generator) so you
can communicate with erlang coroutine using python abstractions :
mailboxwaits for port message in stdin, iterating over messages decoded from binary erlang term format.portwaits forsend(python_struct)(http://docs.python.org/3.3/reference/expressions.html#generator.send) then encodepython_structinto binary term format and send it to the erlang port via stdout.
So for instance, if you want to create a Python script which
receives a 2-tuple of numbers {A, B} and returns {ok, A/B} or {error, divisionbyzero},
then you can use:
fromerlasticimportport_connection, AtomasAmailbox, port=port_connection()
for (a, b) inmailbox:
ifb!=0:
port.send((A("ok"), a/b))
else:
port.send((A("error"), A("divisionbyzero")))Strings are sent as raw bytes with unknown character encoding. To recursively convert data from raw bytes to Python strings, you can pass received data through a function like
defconvert_to_string(data):
ifisinstance(data, bytes): returndata.decode('ascii') # or 'utf-8' ...ifisinstance(data, dict): returndict(map(convert_to_string, data.items()))
ifisinstance(data, tuple): returntuple(map(convert_to_string, data))
ifisinstance(data, list): returnlist(map(convert_to_string, data))
returndataIf the output of Python's print statements aren't showing up in your terminal, try printing to stderr instead, using a function like eprint below:
importsysimportpprintpprinter=pprint.PrettyPrinter(indent=2, stream=sys.stderr, width=80)
defeprint(*args):
pprinter.pprint(args)and at the erlang side, use -u python parameter to prevent python output
buffering, use 4 bytes packet length because it is the configuration used by
the python generators.
Port=open_port({spawn,"python3 -u division.py"},[binary,{packet,4}]),
Div=fun(A,B)->
Port! {self(),{command,term_to_binary({A,B})}},
receive {Port,{data,Bin}}->binary_to_term(Bin) after1000->{error,timeout} endend,
io:format("send {A,B}=~p, python result : ~p~n",[{32,10},Div(32,10)]),
io:format("send {A,B}=~p, python result : ~p~n",[{2,0},Div(2,0)]),
io:format("send {A,B}=~p, python result : ~p~n",[{1,1},Div(1,1)])or in Elixir:
port=Port.open({:spawn,"python3 -u division.py"},[:binary,:exit_status,packet: 4])div=fn(a,b)->sendport,{self(),{:command,:erlang.term_to_binary({a,b})}}receivedo{^port,{_,b}}->binary_to_term(b)after100->{:error,:timeout}endendIO.puts"send {a, b}={32, 10}, python result: #{inspectdiv.(32,10)}"IO.puts"send {a, b}={2, 0}, python result: #{inspectdiv.(2,0)}"IO.puts"send {a, b}={1, 1}, python result: #{inspectdiv.(1,1)}"