diff --git a/lib/livebook/evaluator.ex b/lib/livebook/evaluator.ex index 6146f896b..f6914de13 100644 --- a/lib/livebook/evaluator.ex +++ b/lib/livebook/evaluator.ex @@ -64,10 +64,16 @@ defmodule Livebook.Evaluator do Evaluation response is sent to the process identified by `send_to` as `{:evaluation_response, ref, response}`. Note that response is transformed with the configured formatter (identity by default). + + ## Options + + * `:file` - file to which the evaluated code belongs. Most importantly, + this has an impact on the value of `__DIR__`. """ - @spec evaluate_code(t(), pid(), String.t(), ref(), ref()) :: :ok - def evaluate_code(evaluator, send_to, code, ref, prev_ref \\ :initial) when ref != :initial do - GenServer.cast(evaluator, {:evaluate_code, send_to, code, ref, prev_ref}) + @spec evaluate_code(t(), pid(), String.t(), ref(), ref(), keyword()) :: :ok + def evaluate_code(evaluator, send_to, code, ref, prev_ref \\ :initial, opts \\ []) + when ref != :initial do + GenServer.cast(evaluator, {:evaluate_code, send_to, code, ref, prev_ref, opts}) end @doc """ @@ -107,11 +113,14 @@ defmodule Livebook.Evaluator do end @impl true - def handle_cast({:evaluate_code, send_to, code, ref, prev_ref}, state) do + def handle_cast({:evaluate_code, send_to, code, ref, prev_ref, opts}, state) do Evaluator.IOProxy.configure(state.io_proxy, send_to, ref) context = Map.get(state.contexts, prev_ref, state.contexts.initial) + file = Keyword.get(opts, :file, "nofile") + context = put_in(context.env.file, file) + case eval(code, context.binding, context.env) do {:ok, result, binding, env} -> result_context = %{binding: binding, env: env} diff --git a/lib/livebook/runtime.ex b/lib/livebook/runtime.ex index 51090e0b2..23be0159a 100644 --- a/lib/livebook/runtime.ex +++ b/lib/livebook/runtime.ex @@ -41,9 +41,21 @@ defprotocol Livebook.Runtime do * `{:evaluation_stdout, ref, string}` - output captured during evaluation * `{:evaluation_response, ref, response}` - final result of the evaluation + + ## Options + + * `:file` - file to which the evaluated code belongs. Most importantly, + this has an impact on the value of `__DIR__`. """ - @spec evaluate_code(t(), String.t(), ref(), ref(), ref()) :: :ok - def evaluate_code(runtime, code, container_ref, evaluation_ref, prev_evaluation_ref \\ :initial) + @spec evaluate_code(t(), String.t(), ref(), ref(), ref(), keyword()) :: :ok + def evaluate_code( + runtime, + code, + container_ref, + evaluation_ref, + prev_evaluation_ref \\ :initial, + opts \\ [] + ) @doc """ Disposes of evaluation identified by the given ref. diff --git a/lib/livebook/runtime/attached.ex b/lib/livebook/runtime/attached.ex index 7cbadefd6..13bd77388 100644 --- a/lib/livebook/runtime/attached.ex +++ b/lib/livebook/runtime/attached.ex @@ -49,13 +49,21 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Attached do ErlDist.Manager.stop(runtime.node) end - def evaluate_code(runtime, code, container_ref, evaluation_ref, prev_evaluation_ref \\ :initial) do + def evaluate_code( + runtime, + code, + container_ref, + evaluation_ref, + prev_evaluation_ref \\ :initial, + opts \\ [] + ) do ErlDist.Manager.evaluate_code( runtime.node, code, container_ref, evaluation_ref, - prev_evaluation_ref + prev_evaluation_ref, + opts ) end diff --git a/lib/livebook/runtime/elixir_standalone.ex b/lib/livebook/runtime/elixir_standalone.ex index 28d636bfa..d09ecc7ab 100644 --- a/lib/livebook/runtime/elixir_standalone.ex +++ b/lib/livebook/runtime/elixir_standalone.ex @@ -73,13 +73,21 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.ElixirStandalone do ErlDist.Manager.stop(runtime.node) end - def evaluate_code(runtime, code, container_ref, evaluation_ref, prev_evaluation_ref \\ :initial) do + def evaluate_code( + runtime, + code, + container_ref, + evaluation_ref, + prev_evaluation_ref \\ :initial, + opts \\ [] + ) do ErlDist.Manager.evaluate_code( runtime.node, code, container_ref, evaluation_ref, - prev_evaluation_ref + prev_evaluation_ref, + opts ) end diff --git a/lib/livebook/runtime/erl_dist/manager.ex b/lib/livebook/runtime/erl_dist/manager.ex index 40b5bf2b5..53b485d1e 100644 --- a/lib/livebook/runtime/erl_dist/manager.ex +++ b/lib/livebook/runtime/erl_dist/manager.ex @@ -49,12 +49,25 @@ defmodule Livebook.Runtime.ErlDist.Manager do See `Evaluator` for more details. """ - @spec evaluate_code(node(), String.t(), Evaluator.ref(), Evaluator.ref(), Evaluator.ref()) :: - :ok - def evaluate_code(node, code, container_ref, evaluation_ref, prev_evaluation_ref \\ :initial) do + @spec evaluate_code( + node(), + String.t(), + Evaluator.ref(), + Evaluator.ref(), + Evaluator.ref(), + keyword() + ) :: :ok + def evaluate_code( + node, + code, + container_ref, + evaluation_ref, + prev_evaluation_ref \\ :initial, + opts \\ [] + ) do GenServer.cast( {@name, node}, - {:evaluate_code, code, container_ref, evaluation_ref, prev_evaluation_ref} + {:evaluate_code, code, container_ref, evaluation_ref, prev_evaluation_ref, opts} ) end @@ -153,7 +166,7 @@ defmodule Livebook.Runtime.ErlDist.Manager do end def handle_cast( - {:evaluate_code, code, container_ref, evaluation_ref, prev_evaluation_ref}, + {:evaluate_code, code, container_ref, evaluation_ref, prev_evaluation_ref, opts}, state ) do state = ensure_evaluator(state, container_ref) @@ -163,7 +176,8 @@ defmodule Livebook.Runtime.ErlDist.Manager do state.owner, code, evaluation_ref, - prev_evaluation_ref + prev_evaluation_ref, + opts ) {:noreply, state} diff --git a/lib/livebook/runtime/mix_standalone.ex b/lib/livebook/runtime/mix_standalone.ex index 59bfacc78..2b8e51de7 100644 --- a/lib/livebook/runtime/mix_standalone.ex +++ b/lib/livebook/runtime/mix_standalone.ex @@ -109,13 +109,21 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.MixStandalone do ErlDist.Manager.stop(runtime.node) end - def evaluate_code(runtime, code, container_ref, evaluation_ref, prev_evaluation_ref \\ :initial) do + def evaluate_code( + runtime, + code, + container_ref, + evaluation_ref, + prev_evaluation_ref \\ :initial, + opts \\ [] + ) do ErlDist.Manager.evaluate_code( runtime.node, code, container_ref, evaluation_ref, - prev_evaluation_ref + prev_evaluation_ref, + opts ) end diff --git a/lib/livebook/session.ex b/lib/livebook/session.ex index e950abe3f..7b245ddd0 100644 --- a/lib/livebook/session.ex +++ b/lib/livebook/session.ex @@ -546,7 +546,10 @@ defmodule Livebook.Session do [] -> :initial end - Runtime.evaluate_code(state.data.runtime, cell.source, :main, cell.id, prev_ref) + file = (state.data.path || "") <> "#cell" + opts = [file: file] + + Runtime.evaluate_code(state.data.runtime, cell.source, :main, cell.id, prev_ref, opts) state end diff --git a/test/livebook/evaluator_test.exs b/test/livebook/evaluator_test.exs index f12b24188..f3188f706 100644 --- a/test/livebook/evaluator_test.exs +++ b/test/livebook/evaluator_test.exs @@ -8,7 +8,7 @@ defmodule Livebook.EvaluatorTest do %{evaluator: evaluator} end - describe "evaluate_code/4" do + describe "evaluate_code/6" do test "given a valid code returns evaluation result", %{evaluator: evaluator} do code = """ x = 1 @@ -107,6 +107,17 @@ defmodule Livebook.EvaluatorTest do 1000 end) end + + test "given file option sets it in evaluation environment", %{evaluator: evaluator} do + code = """ + __DIR__ + """ + + opts = [file: "/path/dir/file"] + Evaluator.evaluate_code(evaluator, self(), code, :code_1, :initial, opts) + + assert_receive {:evaluation_response, :code_1, {:ok, "/path/dir"}} + end end describe "forget_evaluation/2" do diff --git a/test/livebook/runtime/erl_dist/manager_test.exs b/test/livebook/runtime/erl_dist/manager_test.exs index 6e69e9e45..0f101d051 100644 --- a/test/livebook/runtime/erl_dist/manager_test.exs +++ b/test/livebook/runtime/erl_dist/manager_test.exs @@ -28,7 +28,7 @@ defmodule Livebook.Runtime.ErlDist.ManagerTest do end end - describe "evaluate_code/2" do + describe "evaluate_code/6" do test "spawns a new evaluator when necessary" do Manager.start() Manager.set_owner(node(), self()) diff --git a/test/support/single_evaluator_runtime.ex b/test/support/single_evaluator_runtime.ex index 2203e7528..e90dd5d0a 100644 --- a/test/support/single_evaluator_runtime.ex +++ b/test/support/single_evaluator_runtime.ex @@ -30,9 +30,17 @@ defimpl Livebook.Runtime, for: LivebookTest.Runtime.SingleEvaluator do code, _container_ref, evaluation_ref, - prev_evaluation_ref \\ :initial + prev_evaluation_ref \\ :initial, + opts \\ [] ) do - Evaluator.evaluate_code(runtime.evaluator, self(), code, evaluation_ref, prev_evaluation_ref) + Evaluator.evaluate_code( + runtime.evaluator, + self(), + code, + evaluation_ref, + prev_evaluation_ref, + opts + ) :ok end