Skip to content

UTF8-ready fork: Erlang binary term codec and port interface. Modeled after Erlectricity.

License

Notifications You must be signed in to change notification settings

skosch/python-erlastic

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Erlastic

Installation

pip3 install git+https://github.com/skosch/python-erlastic

Usage

Erlastic allows you to serialize/deserialize python objects into erlang binary term.

Basic usage is :

import erlastic
py_struct = erlastic.decode(binary_term)
binary = erlastic.encode(py_struct)

Sending and receiving from Python

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 :

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:

from erlastic import port_connection, Atom as A
mailbox, port = port_connection()

for (a, b) in mailbox:
  if b != 0:
    port.send((A("ok"), a / b))
  else:
    port.send((A("error"), A("divisionbyzero")))

Converting between bytes and strings

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

def convert_to_string(data):
    if isinstance(data, bytes):  return data.decode('ascii') # or 'utf-8' ...
    if isinstance(data, dict):   return dict(map(convert_to_string, data.items()))
    if isinstance(data, tuple):  return tuple(map(convert_to_string, data))
    if isinstance(data, list):   return list(map(convert_to_string, data))
    return data

Printing to the terminal from iex

If 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:

import sys
import pprint

pprinter = pprint.PrettyPrinter(indent=2, stream=sys.stderr, width=80)
def eprint(*args):
    pprinter.pprint(args)

Sending and receiving from Erlang or Elixir

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) after 1000->{error,timeout} end
end,
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) ->
  send port, {self(), {:command, :erlang.term_to_binary({a, b})}}

  receive do
    {^port, {_, b}} -> binary_to_term(b)
  after
    100 -> {:error, :timeout}
  end
end

IO.puts "send {a, b}={32, 10}, python result: #{inspect div.(32, 10)}"
IO.puts "send {a, b}={2, 0}, python result: #{inspect div.(2, 0)}"
IO.puts "send {a, b}={1, 1}, python result: #{inspect div.(1, 1)}"

About

UTF8-ready fork: Erlang binary term codec and port interface. Modeled after Erlectricity.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 80.9%
  • Shell 17.9%
  • Makefile 1.2%