From b07752fc55489c29243282e0fa43e91ecb2ce5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 27 Feb 2025 05:50:17 +0100 Subject: [PATCH] Do not include undefined Elixir variables in the expanded ~PY sigil (#12) --- lib/pythonx.ex | 12 ++++++++++-- test/pythonx_test.exs | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/pythonx.ex b/lib/pythonx.ex index e9f9fc2..b92f6ba 100644 --- a/lib/pythonx.ex +++ b/lib/pythonx.ex @@ -281,9 +281,17 @@ defmodule Pythonx do defmacro sigil_PY({:<<>>, _meta, [code]}, []) when is_binary(code) do %{referenced: referenced, defined: defined} = Pythonx.AST.scan_globals(code) + caller = __CALLER__ + globals_entries = - for name <- referenced do - {name, {String.to_atom(name), [], nil}} + for name <- referenced, + name_atom = String.to_atom(name), + # We only reference variables that are actually defined. + # This way, if an undefined variable is referenced in the + # Python code, it results in an informative Python error, + # rather than Elixir compile error. + Macro.Env.has_var?(caller, {name_atom, nil}) do + {name, {name_atom, [], nil}} end assignments = diff --git a/test/pythonx_test.exs b/test/pythonx_test.exs index dc42ef0..b4b04bd 100644 --- a/test/pythonx_test.exs +++ b/test/pythonx_test.exs @@ -374,6 +374,21 @@ defmodule PythonxTest do assert Keyword.keys(binding) == [:x] end + test "results in a Python error when a variable is undefined" do + assert_raise Pythonx.Error, ~r/NameError: name 'x' is not defined/, fn -> + Code.eval_string( + ~S''' + import Pythonx + + ~PY""" + x + 1 + """ + ''', + [] + ) + end + end + test "global redefinition" do {_result, binding} = Code.eval_string( @@ -414,7 +429,7 @@ defmodule PythonxTest do assert repr(result) == "43" end - test "does not result in unused variables" do + test "does not result in unused variables diagnostics" do {_result, diagnostics} = Code.with_diagnostics(fn -> Code.eval_string(~s'''