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)