From 4ecb43eb489bea23459703602e006e2727d46568 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sun, 5 May 2024 21:16:53 -0400 Subject: [PATCH] grass.script: Get GISBASE automatically in get_commands (#3683) * 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). * Add mock for tests --- python/grass/script/core.py | 14 +++++- python/grass/script/tests/conftest.py | 20 ++++++++ .../tests/grass_script_core_get_commands.py | 47 +++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 python/grass/script/tests/conftest.py 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 afac1b15ac1..59d96577a4d 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/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) 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)