From 7cfab051c13735fff045d6012573fa8dc875b8bc Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 3 May 2024 14:16:30 -0400 Subject: [PATCH 1/2] grass.script: Get GISBASE automatically in get_commands The _grass.script.get_commands_ function requires GISBASE (more specifically, path to binary executables and scripts). The need for GISBASE prevents its use before a session (more specifically the runtime part of it) is created. This changes the function to get the GISBASE automatically when not set. It also adds the standard env parameter other functions have (which is useful mainly for tests). --- python/grass/script/core.py | 14 +++++- .../tests/grass_script_core_get_commands.py | 47 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 python/grass/script/tests/grass_script_core_get_commands.py diff --git a/python/grass/script/core.py b/python/grass/script/core.py index 64355616954..c752cf47d75 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -128,7 +128,7 @@ def _make_unicode(val, enc): return decode(val, encoding=enc) -def get_commands(): +def get_commands(*, env=None): """Create list of available GRASS commands to use when parsing string from the command line @@ -141,7 +141,17 @@ def get_commands(): ['d.barscale', 'd.colorlist', 'd.colortable', 'd.correlate', 'd.erase'] """ - gisbase = os.environ["GISBASE"] + if not env: + env = os.environ + gisbase = env.get("GISBASE") + + # Lazy-importing to avoid circular dependencies. + # pylint: disable=import-outside-toplevel + if not gisbase: + from grass.script.setup import get_install_path + + gisbase = get_install_path() + cmd = list() scripts = {".py": list()} if sys.platform == "win32" else {} diff --git a/python/grass/script/tests/grass_script_core_get_commands.py b/python/grass/script/tests/grass_script_core_get_commands.py new file mode 100644 index 00000000000..afb2f6c62dc --- /dev/null +++ b/python/grass/script/tests/grass_script_core_get_commands.py @@ -0,0 +1,47 @@ +"""Test grass.script.core.get_commands function""" + +import sys + +import pytest + +import grass.script as gs + + +def common_test_code(executables_set, scripts_dict): + """Assert results + + Assuming we should always have some executables on path for this to pass a test. + """ + assert len(executables_set) + assert "g.region" in executables_set + if sys.platform == "win32": + assert len(scripts_dict.items()) + # Just in case this is not a standard dictionary object. + assert len(scripts_dict.keys()) == len(scripts_dict.items()) + assert "r.shade" in scripts_dict["py"] + else: + assert "r.shade" in executables_set + + +def test_no_explicit_session(): + """Test without explicit session setup here""" + executables_set, scripts_dict = gs.get_commands() + common_test_code(executables_set, scripts_dict) + + +@pytest.mark.usefixtures("mock_no_session") +def test_no_session_mocked(): + """Test with mocked no session""" + executables_set, scripts_dict = gs.get_commands() + common_test_code(executables_set, scripts_dict) + + +@pytest.mark.usefixtures("mock_no_session") +def test_in_session(tmp_path): + """Test with mocked no session""" + # TODO: Use env=os.environ.copy() + project = tmp_path / "project" + gs.create_project(project) + with gs.setup.init(project) as session: + executables_set, scripts_dict = gs.get_commands(env=session.env) + common_test_code(executables_set, scripts_dict) From b5d8256def0233771e78c81f42b1e879aef3702e Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Fri, 3 May 2024 14:24:03 -0400 Subject: [PATCH 2/2] Add mock for tests --- python/grass/script/tests/conftest.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 python/grass/script/tests/conftest.py diff --git a/python/grass/script/tests/conftest.py b/python/grass/script/tests/conftest.py new file mode 100644 index 00000000000..ce666fbdeef --- /dev/null +++ b/python/grass/script/tests/conftest.py @@ -0,0 +1,20 @@ +"""Fixtures for grass.script""" + +import pytest + + +@pytest.fixture +def mock_no_session(monkeypatch): + """Set the environment variables as if there would be no background session. + + Use with usefixtures (not as a paramter) to avoid warnings about an unused + parameter:: + + @pytest.mark.usefixtures("mock_no_session") + def test_session_handling(): + pass + + There may or may not be a session in the background (we don't check either way). + """ + monkeypatch.delenv("GISRC", raising=False) + monkeypatch.delenv("GISBASE", raising=False)