Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -459,11 +459,15 @@ import_module_from_file and to_py_list_str

``import_module_from_file`` imports any Python file as a module by path (also
taking an optional :ref:`session <python_bridge:sessions>` parameter), and is
what ``import_run_script`` uses internally:
what ``import_run_script`` uses internally. Like the file name of
``exec_file``, a relative path is relative to the directory of the testbench
file, ``tb_path``, so ``test_runner_setup`` must have been called; this works
from verification components without ``runner_cfg``. An absolute path is used
as it is given:

.. code-block:: vhdl

import_module_from_file(join(tb_path(runner_cfg), "reference_model.py"), "reference_model");
import_module_from_file("reference_model.py", "reference_model");
exec("reference_model.configure(gain=4)");

``to_py_list_str`` converts an ``integer_vector``, ``integer_vector_ptr_t`` or
Expand Down
3 changes: 3 additions & 0 deletions release_notes/0.2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Changes

- `import_module_from_file` resolves a relative path against the directory of the testbench file, like `exec_file`, rather than against the working directory of the simulator. It must be called after `test_runner_setup`. Callers that relied on a path relative to the working directory must pass an absolute path instead. See #7.
12 changes: 9 additions & 3 deletions src/vunit_python_bridge/hdl/src/python_pkg.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ use vunit_lib.logger_pkg.all;
------------------------------------------------------------------------------

package python_pkg is
-- Import a Python file as a module. A relative path is relative to the
-- directory of the testbench file, tb_path, so test_runner_setup must have
-- been called. An absolute path is used as it is given.
procedure import_module_from_file(
module_path, as_module_name : string; session : python_session_t := default_session
);
Expand Down Expand Up @@ -274,6 +277,8 @@ package python_pkg is
end package;

package body python_pkg is
impure function p_file_path(file_name : string) return string;

-- @formatter:off
procedure import_module_from_file(
module_path, as_module_name : string; session : python_session_t := default_session
Expand All @@ -283,7 +288,7 @@ package body python_pkg is
"from importlib.util import spec_from_file_location, module_from_spec" & LF &
"from pathlib import Path" & LF &
"import sys" & LF &
spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" & module_path & "')))" & LF &
spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" & p_file_path(module_path) & "')))" & LF &
as_module_name & " = module_from_spec(" & spec_name & ")" & LF &
"sys.modules['" & as_module_name & "'] = " & as_module_name & LF &
spec_name & ".loader.exec_module(" & as_module_name & ")";
Expand Down Expand Up @@ -893,8 +898,9 @@ package body python_pkg is
return false;
end;

-- The file name of exec_file, a relative one taken from the directory of the
-- testbench file like the file names of the other VUnit subprograms
-- The file name of exec_file and import_module_from_file, a relative one taken
-- from the directory of the testbench file like the file names of the other
-- VUnit subprograms
impure function p_file_path(file_name : string) return string is
begin
if p_is_absolute(file_name) then
Expand Down
12 changes: 9 additions & 3 deletions src/vunit_python_bridge/hdl/tools/python_pkg.vhd.in
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ use vunit_lib.logger_pkg.all;
------------------------------------------------------------------------------

package python_pkg is
-- Import a Python file as a module. A relative path is relative to the
-- directory of the testbench file, tb_path, so test_runner_setup must have
-- been called. An absolute path is used as it is given.
procedure import_module_from_file(
module_path, as_module_name : string; session : python_session_t := default_session
);
Expand Down Expand Up @@ -176,6 +179,8 @@ $call_declarations
end package;

package body python_pkg is
impure function p_file_path(file_name : string) return string;

-- @formatter:off
procedure import_module_from_file(
module_path, as_module_name : string; session : python_session_t := default_session
Expand All @@ -185,7 +190,7 @@ package body python_pkg is
"from importlib.util import spec_from_file_location, module_from_spec" & LF &
"from pathlib import Path" & LF &
"import sys" & LF &
spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" & module_path & "')))" & LF &
spec_name & " = spec_from_file_location('" & as_module_name & "', str(Path('" & p_file_path(module_path) & "')))" & LF &
as_module_name & " = module_from_spec(" & spec_name & ")" & LF &
"sys.modules['" & as_module_name & "'] = " & as_module_name & LF &
spec_name & ".loader.exec_module(" & as_module_name & ")";
Expand Down Expand Up @@ -737,8 +742,9 @@ $arg_functions
return false;
end;

-- The file name of exec_file, a relative one taken from the directory of the
-- testbench file like the file names of the other VUnit subprograms
-- The file name of exec_file and import_module_from_file, a relative one taken
-- from the directory of the testbench file like the file names of the other
-- VUnit subprograms
impure function p_file_path(file_name : string) return string is
begin
if p_is_absolute(file_name) then
Expand Down
16 changes: 16 additions & 0 deletions tests/tb_python_pkg_bridge.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ begin
check_equal(integer'(call("bump", data)), 5);
end;

-- Imports the way a verification component would, without runner_cfg
procedure import_filters is
begin
import_module_from_file("models/filters.py", "filters_model");
end;

begin
test_runner_setup(runner, runner_cfg);

Expand Down Expand Up @@ -219,6 +225,16 @@ begin
exec_file("models/counter.py");
check_equal(integer'(call("get_call_count")), 2);

elsif run("Test importing a module with a relative file name") then
-- The simulator runs in the VUnit output path, not the testbench directory
check_false(eval_boolean("__import__('os').path.samefile('.', '" & tb_path(runner_cfg) & "')"));
import_filters;
check_equal(integer'(call("filters_model.fir", arg(2))), 3);

elsif run("Test importing a module with an absolute file name") then
import_module_from_file(join(tb_path(runner_cfg), "models/filters.py"), "filters_model");
check_equal(integer'(call("filters_model.fir", arg(2))), 3);

elsif run("Test that exec_file with a missing file fails") then
-- The error message shows the path in the native format of the OS
exec("import os" + "def native_repr(path):" + " return repr(os.path.normpath(path))");
Expand Down
Loading