From cb01cddef9defac9ba04f68fa4f45da83d45c066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 27 Feb 2025 04:20:51 +0900 Subject: [PATCH 1/3] Do not include undefined Elixir variables in the expanded ~PY sigil --- lib/pythonx.ex | 12 ++++++++++-- test/pythonx_test.exs | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/pythonx.ex b/lib/pythonx.ex index e9f9fc2..58206a9 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) + versioned_vars = __CALLER__.versioned_vars + 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. + Map.has_key?(versioned_vars, {name_atom, nil}) do + {name, {name_atom, [], nil}} end assignments = diff --git a/test/pythonx_test.exs b/test/pythonx_test.exs index dc42ef0..209b148 100644 --- a/test/pythonx_test.exs +++ b/test/pythonx_test.exs @@ -374,6 +374,23 @@ 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 +431,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''' From 4870be26bb538e09670a67fff20604eff6ac3998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 27 Feb 2025 04:43:28 +0900 Subject: [PATCH 2/3] Format --- lib/pythonx.ex | 12 ++++++------ test/pythonx_test.exs | 4 +--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/pythonx.ex b/lib/pythonx.ex index 58206a9..96fedd5 100644 --- a/lib/pythonx.ex +++ b/lib/pythonx.ex @@ -285,12 +285,12 @@ defmodule Pythonx do globals_entries = 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. - Map.has_key?(versioned_vars, {name_atom, nil}) do + 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. + Map.has_key?(versioned_vars, {name_atom, nil}) do {name, {name_atom, [], nil}} end diff --git a/test/pythonx_test.exs b/test/pythonx_test.exs index 209b148..b4b04bd 100644 --- a/test/pythonx_test.exs +++ b/test/pythonx_test.exs @@ -386,9 +386,7 @@ defmodule PythonxTest do ''', [] ) - - end - + end end test "global redefinition" do From 036345cdd2a6d2e06cf416b194f941b4a11f9253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Thu, 27 Feb 2025 13:45:15 +0900 Subject: [PATCH 3/3] Up --- lib/pythonx.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pythonx.ex b/lib/pythonx.ex index 96fedd5..b92f6ba 100644 --- a/lib/pythonx.ex +++ b/lib/pythonx.ex @@ -281,7 +281,7 @@ defmodule Pythonx do defmacro sigil_PY({:<<>>, _meta, [code]}, []) when is_binary(code) do %{referenced: referenced, defined: defined} = Pythonx.AST.scan_globals(code) - versioned_vars = __CALLER__.versioned_vars + caller = __CALLER__ globals_entries = for name <- referenced, @@ -290,7 +290,7 @@ defmodule Pythonx do # This way, if an undefined variable is referenced in the # Python code, it results in an informative Python error, # rather than Elixir compile error. - Map.has_key?(versioned_vars, {name_atom, nil}) do + Macro.Env.has_var?(caller, {name_atom, nil}) do {name, {name_atom, [], nil}} end