Diagnosing port communications issue

defmodule PythonProcessAPI do
  require Logger

  def process_output(foo, _, python_path) do
    Print.text("Results received from #{python_path}.")

    Logger.info("#{inspect(foo)}, __MODULE__, line 8")
  end

  def connect(data, python_path) do
    port =
      :erlang.open_port(
        {:spawn, python_path},
        [:binary, packet: 4]
      )

    execute = fn input ->
      send(port, {self(), {:command, :erlang.term_to_binary({input})}})

      receive do
        foo ->
          process_output(foo, data, python_path)
      end
    end

    execute.(data)
  end
end
#my_python_process.py

import sys
import simplejson as json
from loguru import logger
from conversion import to_string
from erlastic import port_connection, Atom as atom
from some_library.errors import (BaseError, AuthenticationError, PermissionDenied,
                                ArgumentsRequired, BadRequest, BadResponse,
                                NullResponse, InvalidAddress, NotSupported, NetworkError, DDoSProtection,
                                RateLimitExceeded, OnMaintenance, InvalidNonce, RequestTimeout)


mailbox, port = port_connection()


@logger.catch
def submit_report(success_status, report, arg1, arg2, arg3):
    logger.info("Processing report . . . ")
    full_report = report | {
        'arg1': arg1, 'arg2': arg2, 'arg3': arg3}
    full_report_json = json.dumps(full_report)

    try:
        port.send((atom(success_status), (full_report_json)))
        logger.info("Transmitted report . . . ")
    except (BrokenPipeError) as error:
        logger.exception(error)
        sys.exit()
    finally:
        sys.exit()


@logger.catch
def fetch_params():
    for params_data in mailbox:
        params_hash = to_string(params_data)
        return params_hash[0]


@logger.catch
def some_function():
    params_json = fetch_params()
    params = json.loads(params_json)
   
   arg1, arg2, arg3 = params

    try:
        submission_report = some_operation(params)

    except (BaseError, AuthenticationError, PermissionDenied,
            ArgumentsRequired, BadRequest, BadResponse,
            NullResponse, InvalidAddress, NotSupported, NetworkError, DDoSProtection,
            RateLimitExceeded, OnMaintenance, InvalidNonce, RequestTimeout) as error:

        logger.exception(error)
        logger.info(params)
        submit_report(
            'error', {'error': str(error)}, arg1, arg2, arg3)
    except Exception as crash_report:
        logger.exception(crash_report)
        logger.info(params)
        submit_report('error', {'error': str(
            crash_report)}, arg1, arg2, arg3)
    else:
        submit_report(
            'ok', submission_report, arg1, arg2, arg3)


@ logger.catch
def main():
    some_function()


if __name__ == '__main__':
    main()

The problem we’re having is that data can be passed to the Python process successfully but the return message never reaches the Elixir side. All success messages from the Python end are logged, but there are no exceptions or error messages to go on. We’re at a loss for how to diagnose what’s wrong. Suggestions? Recommendations?

Can’t comment on the python side.

You are only handling one message in the elixir side. Is it intentional? Maybe the message is still in the inbox. try flush() in iex.

Can’t comment on erlastic, but I have been using erlport lately with python.

This blog post is really nice…

1 Like