anchor.diff:      gen_server:start_link(?MODULE, Options, [{timeout, ?START_TIMEOUT}]).
   
   
  -stop(InstanceId) when is_pid(InstanceId) ->
  -    gen_server:cast(InstanceId, stop).
  -
  -
  -call(InstanceId, Module, Function, Args) when is_pid(InstanceId),
  +-spec stop(Instance) -> ok when
  +    Instance :: instance().
  +
  +stop(Instance) when is_pid(Instance) ->
  +    gen_server:cast(Instance, stop).
  +
  +
  +-spec call(Instance, Module, Function, Args) -> Result when
  +    Instance :: instance(),
  +    Module :: atom(),
  +    Function :: atom(),
  +    Args :: list(),
  +    Result :: term().
  +
  +% TODO: Call timeout?
  +call(Instance, Module, Function, Args) when is_pid(Instance),
           is_atom(Module), is_atom(Function), is_list(Args) ->
  -    case gen_server:call(InstanceId, {call, Module, Function, Args}) of
  +    case gen_server:call(Instance, {call, Module, Function, Args}) of
           {ok, Result} ->
               Result;
           {error, Error} ->
  
positive.code:     end.
  
  
  cast(InstanceId, Module, Function, Args) when is_pid(InstanceId),
          is_atom(Module), is_atom(Function), is_list(Args) ->
      gen_server:cast(InstanceId, {call, Module, Function, Args}).
  
  
  %%%
  %%% Behaviour callbacks
  %%%
  
  init(Options) when is_list(Options) ->
      % FIXME: Cleanup option parsing and command line construction
      Stdio = case proplists:get_value(nouse_stdio, Options, false) of
          true ->
              nouse_stdio;
          false ->
              use_stdio
      end,
      Packet = case proplists:get_value(packet, Options, 4) of
          1 ->
              1;
          2 ->
              2;
          4 ->
              4
      end,
      Python = proplists:get_value(python, Options, "python"),
      Env = proplists:get_value(env, Options, []),
      % TODO: Check for errors
      PrivPath = filename:join(code:priv_dir(erlport), "python"),
      Env2 = case proplists:get_value(python_path, Options) of
          undefined ->
              [{"PYTHONPATH", PrivPath} | Env];
          PythonPath ->
              [{"PYTHONPATH", PrivPath ++ ":" ++ PythonPath}
                  | proplists:delete("PYTHONPATH", Env)]
      end,
      % TODO: Add custom args?
      Path = lists:concat([Python, " -u -m erlport.cli --packet=", Packet,
          " --", Stdio]),
      Port = open_port({spawn, Path},
          [{packet, Packet}, binary, Stdio, hide, {env, Env2}]),
      {ok, #state{port=Port}}.
  
  
  handle_call({call, Module, Function, Args}, From, State=#state{port=Port,
          id=Id, requests=Requests})
          when is_atom(Module), is_atom(Function), is_list(Args) ->
      Timer = erlang:send_after(?CALL_TIMEOUT, self(), {timeout, From}),
      NewRequests = dict:store(Id, {From, Timer}, Requests),
      Request = {'S', Id, Module, Function, Args},
      true = port_command(Port, term_to_binary(Request)),
      % TODO: Optimize Id generation
      % TODO: Cleanup requests storage
      {noreply, State#state{id=Id + 1, requests=NewRequests}};
  handle_call(Request, From, State) ->
      {reply, {error, {badarg, ?MODULE, Request, From}}, State}.
  
  
  handle_cast({call, Module, Function, Args}, State=#state{port=Port})
          when is_atom(Module), is_atom(Function), is_list(Args) ->
      Request = {'A', Module, Function, Args},
      true = port_command(Port, term_to_binary(Request)),
      {noreply, State};
  handle_cast(stop, State) ->
      {stop, normal, State};
  handle_cast(_Request, State) ->
      {noreply, State}.
  
  
  handle_info({Port, {data, Data}}, State=#state{port=Port,
          requests=Requests}) ->
      try binary_to_term(Data) of
          {'R', Id, Result} ->
              NewState = case dict:find(Id, Requests) of
                  {ok, {From, Timer}} ->
                      erlang:cancel_timer(Timer),
                      gen_server:reply(From, Result),
                      State#state{requests=dict:erase(Id, Requests)};
                  error ->
                      State
              end,
              {noreply, NewState};
          {'A', Module, Function, Args} when is_atom(Module), is_atom(Function),
                  is_list(Args) ->
              proc_lib:spawn(fun () ->
                  apply(Module, Function, Args)
                  end),
              {noreply, State};
          {'S', Id, Module, Function, Args} when is_atom(Module),
                  is_atom(Function), is_list(Args) ->
              proc_lib:spawn(fun () ->
                  Result = try {ok, apply(Module, Function, Args)}
                      catch
                          Class:Reason ->
                              {error, Class, Reason, erlang:get_stacktrace()}
                      end,
                  Response = {'R', Id, Result},
                  true = port_command(Port, term_to_binary(Response))
                  end),
              {noreply, State}
      catch
          error:badarg ->
              {noreply, State}
      end;
  handle_info({timeout, From}, State) ->
      gen_server:reply(From, {error, timeout}),
      {noreply, State};
  handle_info(_Info, State) ->
      {noreply, State}.
  
  
negative.code: 
  -record(state, {
      port :: port(),
      server = true :: boolean(),
      queue = queue:new() :: queue(),
      in_process
      }).
  
  -type state() :: #state{}.
  -type option() :: client
      | nouse_stdio
      | {packet, 1 | 2 | 4}
      | {python, Python :: string()}
      | {python_path, Path :: string()}
      | {env, [{Name :: string(), Value :: string() | false}]}.
  -type options() :: [option()].
  -opaque instance() :: pid().
  
  -export_type([option/0, options/0]).
  
  -define(START_TIMEOUT, 15000).
  -define(CALL_TIMEOUT, 15000).
  