From a7b738fdcdd12e07e7ccc376040d77159830195c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 27 Feb 2025 14:02:48 +0900 Subject: [PATCH] Improve Python evaluation to raise a Python error for undefined variables --- lib/livebook/runtime/evaluator.ex | 7 +++---- mix.lock | 2 +- test/livebook/runtime/evaluator_test.exs | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/livebook/runtime/evaluator.ex b/lib/livebook/runtime/evaluator.ex index 1931a56c9..c7ae1e366 100644 --- a/lib/livebook/runtime/evaluator.ex +++ b/lib/livebook/runtime/evaluator.ex @@ -941,7 +941,7 @@ defmodule Livebook.Runtime.Evaluator do {result, _diagnostics} = Code.with_diagnostics([log: true], fn -> try do - quoted = python_code_to_quoted(code) + quoted = python_code_to_quoted(code, env) {value, binding, env} = Code.eval_quoted_with_env(quoted, binding, env, prune_binding: true) @@ -992,16 +992,15 @@ defmodule Livebook.Runtime.Evaluator do end end - defp python_code_to_quoted(code) do + defp python_code_to_quoted(code, env) do # We expand the sigil upfront, so it is not traced as import usage # during evaluation. quoted = {:sigil_PY, [], [{:<<>>, [], [code]}, []]} - env = Code.env_for_eval([]) - env = env + |> Map.replace!(:tracers, []) |> Map.replace!(:requires, [Pythonx]) |> Map.replace!(:macros, [{Pythonx, [{:sigil_PY, 2}]}]) diff --git a/mix.lock b/mix.lock index 100a1a783..0f7304587 100644 --- a/mix.lock +++ b/mix.lock @@ -47,7 +47,7 @@ "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "pluggable": {:hex, :pluggable, "1.1.0", "7eba3bc70c0caf4d9056c63c882df8862f7534f0145da7ab3a47ca73e4adb1e4", [:mix], [], "hexpm", "d12eb00ea47b21e92cd2700d6fbe3737f04b64e71b63aad1c0accde87c751637"}, "protobuf": {:hex, :protobuf, "0.13.0", "7a9d9aeb039f68a81717eb2efd6928fdf44f03d2c0dfdcedc7b560f5f5aae93d", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "21092a223e3c6c144c1a291ab082a7ead32821ba77073b72c68515aa51fef570"}, - "pythonx": {:hex, :pythonx, "0.4.0", "17528698aef5162ae83d8b4aa9df063cf15bb3acd3caa56b61eefc75ba7bad93", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "801670ef6eb2eff2ff9ec00f70d14d4e4cd0ed0d6af02fe8c8e94954d56078ab"}, + "pythonx": {:hex, :pythonx, "0.4.2", "542667f1bb7d941cbba8a0b042eb487d463bd009f39ed5957a0d26dc6365973d", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "eb0ffda6d480df15d02dd252b5c2bc7058655f4253b36ee24e1ab301fae849ae"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, diff --git a/test/livebook/runtime/evaluator_test.exs b/test/livebook/runtime/evaluator_test.exs index 07748847d..3f63221d0 100644 --- a/test/livebook/runtime/evaluator_test.exs +++ b/test/livebook/runtime/evaluator_test.exs @@ -1617,6 +1617,27 @@ defmodule Livebook.Runtime.EvaluatorTest do assert [{:z, %Pythonx.Object{}}, {:y, %Pythonx.Object{}}, {:x, 1}] = binding end + test "undefined variable error", %{evaluator: evaluator} do + Evaluator.evaluate_code(evaluator, :python, "x + 1", :code_1, []) + + assert_receive {:runtime_evaluation_response, :code_1, error(message), + %{ + code_markers: [ + %{ + line: 1, + description: "NameError: name 'x' is not defined", + severity: :error + } + ] + }} + + assert clean_message(message) == """ + Traceback (most recent call last): + File "", line 1, in + NameError: name 'x' is not defined + """ + end + test "syntax error", %{evaluator: evaluator} do Evaluator.evaluate_code(evaluator, :python, "1 +", :code_1, [])