From 8d9fba20665637ad7f2b8a30773ba23f8bb5bb59 Mon Sep 17 00:00:00 2001 From: Akash Maity Date: Thu, 8 Oct 2026 15:32:33 -0300 Subject: [PATCH 1/2] grass: Report clear error instead of traceback when -c fails --- lib/init/grass.py | 11 ++++- .../testsuite/test_grass_create_project.py | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 lib/init/testsuite/test_grass_create_project.py diff --git a/lib/init/grass.py b/lib/init/grass.py index c52b123ac9d..6f67c6fff47 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -295,7 +295,7 @@ def help_message(default_gui) -> None: flags=_("Flags"), help_flag=_("print this help message"), version_flag=_("show version information and exit"), - create=_("create given database, project or mapset if it doesn't exist"), + create=_("create given project or mapset if it doesn't exist"), exit_after=_("exit after creation of project or mapset. Only with -c flag"), force_removal=_( "force removal of .gislock if exists (use with care!)." @@ -666,15 +666,22 @@ def create_location(gisdbase, location, geostring) -> None: :param geostring: path to a georeferenced file or EPSG code """ import grass.script as gs # pylint: disable=E0611 + from grass.exceptions import ScriptError try: gs.create_project(gisdbase, location, crs=geostring) - except gs.ScriptError as err: + except ScriptError as err: fatal( _("Error creating project: {}").format( err.value.strip('"').strip("'").replace("\\n", os.linesep) ) ) + except OSError as err: + fatal( + _("Cannot create project <{}>: {}: {}").format( + location, err.strerror, err.filename + ) + ) def can_create_location(gisdbase: StrPath, location) -> bool: diff --git a/lib/init/testsuite/test_grass_create_project.py b/lib/init/testsuite/test_grass_create_project.py new file mode 100644 index 00000000000..e63ff3e7b7d --- /dev/null +++ b/lib/init/testsuite/test_grass_create_project.py @@ -0,0 +1,46 @@ +""" +TEST: Test of grass -c error handling + +AUTHOR(S): Akash Maity + +PURPOSE: Test that grass -c reports a clear error instead of a traceback + +SPDX-FileCopyrightText: 2026 Akash Maity +SPDX-FileCopyrightText: GRASS Development Team +SPDX-License-Identifier: GPL-2.0-or-later +""" + +import subprocess +import tempfile +import unittest +from pathlib import Path + + +class TestCreateProjectErrors(unittest.TestCase): + """grass -c should print a clear error, not a traceback""" + + executable = "grass" + + def setUp(self): + # A fresh empty folder for each test, deleted automatically afterwards + self.tmp = tempfile.TemporaryDirectory() + self.tmp_path = Path(self.tmp.name) + + def tearDown(self): + self.tmp.cleanup() + + def test_missing_database_directory(self): + """Missing parent directories give a clear error""" + project = self.tmp_path / "missing" / "deeper" / "myproject" + result = subprocess.run( + [self.executable, "-c", "EPSG:4326", str(project), "-e"], + capture_output=True, + text=True, + ) + self.assertNotEqual(result.returncode, 0) + self.assertNotIn("Traceback", result.stderr) + self.assertIn("Cannot create project", result.stderr) + + +if __name__ == "__main__": + unittest.main() From 05624b3177e97209dc2b1c0c5a7825a7adfc93b0 Mon Sep 17 00:00:00 2001 From: Akash Maity Date: Fri, 9 Oct 2026 01:21:59 -0300 Subject: [PATCH 2/2] tests: Convert -c error test to pytest --- lib/init/tests/grass_create_project_test.py | 21 +++++++++ .../testsuite/test_grass_create_project.py | 46 ------------------- 2 files changed, 21 insertions(+), 46 deletions(-) create mode 100644 lib/init/tests/grass_create_project_test.py delete mode 100644 lib/init/testsuite/test_grass_create_project.py diff --git a/lib/init/tests/grass_create_project_test.py b/lib/init/tests/grass_create_project_test.py new file mode 100644 index 00000000000..640c5da7d99 --- /dev/null +++ b/lib/init/tests/grass_create_project_test.py @@ -0,0 +1,21 @@ +"""Test that grass -c reports a clear error instead of a traceback + +SPDX-FileCopyrightText: 2026 Akash Maity +SPDX-FileCopyrightText: GRASS Development Team +SPDX-License-Identifier: GPL-2.0-or-later +""" + +import subprocess + + +def test_missing_database_directory(tmp_path): + """Missing parent directories give a clear error""" + project = tmp_path / "missing" / "deeper" / "myproject" + result = subprocess.run( + ["grass", "-c", "EPSG:4326", str(project), "-e"], + capture_output=True, + text=True, + ) + assert result.returncode != 0 + assert "Traceback" not in result.stderr + assert "Cannot create project" in result.stderr diff --git a/lib/init/testsuite/test_grass_create_project.py b/lib/init/testsuite/test_grass_create_project.py deleted file mode 100644 index e63ff3e7b7d..00000000000 --- a/lib/init/testsuite/test_grass_create_project.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -TEST: Test of grass -c error handling - -AUTHOR(S): Akash Maity - -PURPOSE: Test that grass -c reports a clear error instead of a traceback - -SPDX-FileCopyrightText: 2026 Akash Maity -SPDX-FileCopyrightText: GRASS Development Team -SPDX-License-Identifier: GPL-2.0-or-later -""" - -import subprocess -import tempfile -import unittest -from pathlib import Path - - -class TestCreateProjectErrors(unittest.TestCase): - """grass -c should print a clear error, not a traceback""" - - executable = "grass" - - def setUp(self): - # A fresh empty folder for each test, deleted automatically afterwards - self.tmp = tempfile.TemporaryDirectory() - self.tmp_path = Path(self.tmp.name) - - def tearDown(self): - self.tmp.cleanup() - - def test_missing_database_directory(self): - """Missing parent directories give a clear error""" - project = self.tmp_path / "missing" / "deeper" / "myproject" - result = subprocess.run( - [self.executable, "-c", "EPSG:4326", str(project), "-e"], - capture_output=True, - text=True, - ) - self.assertNotEqual(result.returncode, 0) - self.assertNotIn("Traceback", result.stderr) - self.assertIn("Cannot create project", result.stderr) - - -if __name__ == "__main__": - unittest.main()