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'''