From 18e28de707bd170f9f192f686f04b6a6aa6a7664 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 11:00:33 +0000 Subject: [PATCH 1/5] libgis: Do not use translated unit and projection names as data G_get_units_name() and G_projection_name() return translated text. Using it as data breaks in a translated locale: - G_database_units_to_meters_factor() compared the translated unit name with English names and returned 0 for projects without a conversion factor (e.g. XY), which breaks r.slope.aspect, r.sim.water and v.to.db. - G_units(NULL) and G_get_units_name() parsed the translated name of the database units to get the units code, which gave an unknown unit when the translation was not the English name (e.g. in French). - m.measure, r3.info, r3.out.netcdf and g.region wrote translated names into JSON, shell style output and files. Add G_get_units_name_untranslated(), G_projection_name_untranslated() and G_database_projection_name_untranslated() and use them for such data. Get the database units by code with G_database_unit(). Rast3d_get_vertical_unit() now returns the English name. --- general/g.region/printwindow.c | 4 +- include/grass/defs/gis.h | 3 + lib/gis/proj2.c | 30 ++++++ lib/gis/proj3.c | 30 +++++- lib/gis/units.c | 163 +++++++++++++++++++-------------- lib/raster3d/headerinfo.c | 4 +- misc/m.measure/main.c | 17 ++-- 7 files changed, 173 insertions(+), 78 deletions(-) diff --git a/general/g.region/printwindow.c b/general/g.region/printwindow.c index 865e33b5ae1..9f38b94f632 100644 --- a/general/g.region/printwindow.c +++ b/general/g.region/printwindow.c @@ -108,7 +108,9 @@ void print_window(struct Cell_head *window, int print_flag, int flat_flag, /* flag.print & flag.gprint */ if (print_flag & PRINT_REG) { - prj = G_database_projection_name(); + /* machine-readable output must not depend on the locale */ + prj = format == PLAIN ? G_database_projection_name() + : G_database_projection_name_untranslated(); if (!prj && format != JSON) prj = "** unknown **"; diff --git a/include/grass/defs/gis.h b/include/grass/defs/gis.h index 106c9801ef4..fc9be60243a 100644 --- a/include/grass/defs/gis.h +++ b/include/grass/defs/gis.h @@ -714,11 +714,13 @@ int G_projection(void); /* proj2.c */ int G_projection_units(int); const char *G_projection_name(int); +const char *G_projection_name_untranslated(int); /* proj3.c */ const char *G_database_unit_name(int); int G_database_unit(void); const char *G_database_projection_name(void); +const char *G_database_projection_name_untranslated(void); const char *G_database_datum_name(void); const char *G_database_ellipse_name(void); double G_database_units_to_meters_factor(void); @@ -846,6 +848,7 @@ void G_trim_decimal(char *); double G_meters_to_units_factor(int); double G_meters_to_units_factor_sq(int); const char *G_get_units_name(int, int, int); +const char *G_get_units_name_untranslated(int, int, int); int G_units(const char *); int G_is_units_type_spatial(int); int G_is_units_type_temporal(int); diff --git a/lib/gis/proj2.c b/lib/gis/proj2.c index a971ecc545f..9ece2f1b7d0 100644 --- a/lib/gis/proj2.c +++ b/lib/gis/proj2.c @@ -45,6 +45,9 @@ int G_projection_units(int n) /*! \brief Get projection name + The name is translated, so it is meant for messages and other text for + people. Use G_projection_name_untranslated() for machine-readable output. + \param n projection code \return projection name @@ -65,3 +68,30 @@ const char *G_projection_name(int n) return NULL; } } + +/*! + \brief Get projection name in English, independent of the locale + + Same as G_projection_name(), but never translated, so the result is + suitable for machine-readable output (JSON, shell style). + + \param n projection code + + \return projection name + \return NULL on error + */ +const char *G_projection_name_untranslated(int n) +{ + switch (n) { + case PROJECTION_XY: + return "x,y"; + case PROJECTION_UTM: + return "UTM"; + case PROJECTION_LL: + return "Latitude-Longitude"; + case PROJECTION_OTHER: + return "Other Projection"; + default: + return NULL; + } +} diff --git a/lib/gis/proj3.c b/lib/gis/proj3.c index 4c1491452c4..ca52b34dce4 100644 --- a/lib/gis/proj3.c +++ b/lib/gis/proj3.c @@ -132,6 +132,33 @@ const char *G_database_projection_name(void) return name; } +/*! + \brief Get projection name for the current location in English + + Same as G_database_projection_name(), but never translated, so the result + is suitable for machine-readable output (JSON, shell style). + + \return projection name + */ +const char *G_database_projection_name_untranslated(void) +{ + int n; + const char *name; + + switch (n = G_projection()) { + case PROJECTION_XY: + case PROJECTION_UTM: + case PROJECTION_LL: + return G_projection_name_untranslated(n); + } + + name = lookup_proj("name"); + if (!name) + return "Unknown projection"; + + return name; +} + /*! \brief Conversion to meters @@ -161,7 +188,8 @@ double G_database_units_to_meters_factor(void) if (buf) sscanf(buf, "%lf", &factor); if (factor <= 0.0) { - unit = G_database_unit_name(0); + /* the table has English names, so the translated name does not match */ + unit = G_get_units_name_untranslated(G_database_unit(), 0, FALSE); for (n = 0; table[n].unit; n++) if (equal(unit, table[n].unit)) { factor = table[n].factor; diff --git a/lib/gis/units.c b/lib/gis/units.c index f3256bf9cbe..ce1819d4da1 100644 --- a/lib/gis/units.c +++ b/lib/gis/units.c @@ -178,209 +178,234 @@ int G_is_units_type_temporal(int units) return 0; } -/*! - \brief Get localized units name - - Units codes (gis.h): - - U_METERS - - U_KILOMETERS - - U_ACRES - - U_HECTARES - - U_MILES - - U_FEET - - U_USFEET - - \param units units code - \param plural plural form if true - \param square area units if true - - \return units name - \return NULL if units not found - */ -const char *G_get_units_name(int units, int plural, int square) +static const char *units_name(int units, int plural, int square, int localize) { switch (units) { case U_UNKNOWN: if (square) return plural // GTC: localized area units - ? _("square units") + ? (localize ? _("square units") : "square units") // GTC: localized area units - : _("square unit"); + : (localize ? _("square unit") : "square unit"); else return plural // GTC: localized units - ? _("units") + ? (localize ? _("units") : "units") // GTC: localized units - : _("unit"); + : (localize ? _("unit") : "unit"); break; case U_METERS: if (square) return plural // GTC: localized area units - ? _("square meters") + ? (localize ? _("square meters") : "square meters") // GTC: localized area units - : _("square meter"); + : (localize ? _("square meter") : "square meter"); else return plural // GTC: localized units - ? _("meters") + ? (localize ? _("meters") : "meters") // GTC: localized units - : _("meter"); + : (localize ? _("meter") : "meter"); break; case U_KILOMETERS: if (square) return plural // GTC: localized area units - ? _("square kilometers") + ? (localize ? _("square kilometers") : "square kilometers") // GTC: localized area units - : _("square kilometer"); + : (localize ? _("square kilometer") : "square kilometer"); else return plural // GTC: localized units - ? _("kilometers") + ? (localize ? _("kilometers") : "kilometers") // GTC: localized units - : _("kilometer"); + : (localize ? _("kilometer") : "kilometer"); break; case U_ACRES: if (square) return plural // GTC: localized area units - ? _("acres") + ? (localize ? _("acres") : "acres") // GTC: localized area units - : _("acre"); + : (localize ? _("acre") : "acre"); else - return G_get_units_name(G_units(G_database_unit_name(1)), plural, - square); + return units_name(G_database_unit(), plural, square, localize); break; case U_HECTARES: if (square) return plural // GTC: localized area units - ? _("hectares") + ? (localize ? _("hectares") : "hectares") // GTC: localized area units - : _("hectare"); + : (localize ? _("hectare") : "hectare"); else - return G_get_units_name(G_units(G_database_unit_name(1)), plural, - square); + return units_name(G_database_unit(), plural, square, localize); break; case U_MILES: if (square) return plural // GTC: localized area units - ? _("square miles") + ? (localize ? _("square miles") : "square miles") // GTC: localized area units - : _("square mile"); + : (localize ? _("square mile") : "square mile"); else return plural // GTC: localized units - ? _("miles") + ? (localize ? _("miles") : "miles") // GTC: localized units - : _("mile"); + : (localize ? _("mile") : "mile"); break; case U_FEET: if (square) return plural // GTC: localized area units, do not confuse with US feet - ? _("square feet") + ? (localize ? _("square feet") : "square feet") // GTC: localized area units, do not confuse with US foot - : _("square foot"); + : (localize ? _("square foot") : "square foot"); else return plural // GTC: localized units, do not confuse with US feet - ? _("feet") + ? (localize ? _("feet") : "feet") // GTC: localized units, do not confuse with US foot - : _("foot"); + : (localize ? _("foot") : "foot"); break; case U_USFEET: if (square) return plural // GTC: localized area units, do not confuse with feet - ? _("square US feet") + ? (localize ? _("square US feet") : "square US feet") // GTC: localized area units, do not confuse with foot - : _("square US foot"); + : (localize ? _("square US foot") : "square US foot"); else return plural // GTC: localized units, do not confuse with feet - ? _("US feet") + ? (localize ? _("US feet") : "US feet") // GTC: localized units, do not confuse with foot - : _("US foot"); + : (localize ? _("US foot") : "US foot"); break; case U_DEGREES: if (square) return plural // GTC: localized area units - ? _("square degrees") + ? (localize ? _("square degrees") : "square degrees") // GTC: localized area units - : _("square degree"); + : (localize ? _("square degree") : "square degree"); else return plural // GTC: localized units - ? _("degrees") + ? (localize ? _("degrees") : "degrees") // GTC: localized units - : _("degree"); + : (localize ? _("degree") : "degree"); break; case U_YEARS: return plural // GTC: localized time units - ? _("years") + ? (localize ? _("years") : "years") // GTC: localized time units - : _("year"); + : (localize ? _("year") : "year"); break; case U_MONTHS: return plural // GTC: localized time units - ? _("months") + ? (localize ? _("months") : "months") // GTC: localized time units - : _("month"); + : (localize ? _("month") : "month"); break; case U_DAYS: return plural // GTC: localized time units - ? _("days") + ? (localize ? _("days") : "days") // GTC: localized time units - : _("day"); + : (localize ? _("day") : "day"); break; case U_HOURS: return plural // GTC: localized time units - ? _("hours") + ? (localize ? _("hours") : "hours") // GTC: localized time units - : _("hour"); + : (localize ? _("hour") : "hour"); break; case U_MINUTES: return plural // GTC: localized time units - ? _("minutes") + ? (localize ? _("minutes") : "minutes") // GTC: localized time units - : _("minute"); + : (localize ? _("minute") : "minute"); break; case U_SECONDS: return plural // GTC: localized time units - ? _("seconds") + ? (localize ? _("seconds") : "seconds") // GTC: localized time units - : _("second"); + : (localize ? _("second") : "second"); break; } return NULL; } +/*! + \brief Get localized units name + + Units codes (gis.h): + - U_METERS + - U_KILOMETERS + - U_ACRES + - U_HECTARES + - U_MILES + - U_FEET + - U_USFEET + + The name is translated, so it is meant for messages and other text for + people. Use G_get_units_name_untranslated() for machine-readable output. + + \param units units code + \param plural plural form if true + \param square area units if true + + \return units name + \return NULL if units not found + */ +const char *G_get_units_name(int units, int plural, int square) +{ + return units_name(units, plural, square, 1); +} + +/*! + \brief Get units name in English, independent of the locale + + Same as G_get_units_name(), but never translated, so the result is suitable + for machine-readable output (JSON, shell style) and for comparison with + names which G_units() recognizes. + + \param units units code + \param plural plural form if true + \param square area units if true + + \return units name + \return NULL if units not found + */ +const char *G_get_units_name_untranslated(int units, int plural, int square) +{ + return units_name(units, plural, square, 0); +} + /*! \brief Get units code by name @@ -404,7 +429,7 @@ const char *G_get_units_name(int units, int plural, int square) int G_units(const char *units_name) { if (units_name == NULL) { - return G_units(G_database_unit_name(1)); + return G_database_unit(); } if (strcasecmp(units_name, "meter") == 0 || diff --git a/lib/raster3d/headerinfo.c b/lib/raster3d/headerinfo.c index d89f9ec372d..2a8729488c5 100644 --- a/lib/raster3d/headerinfo.c +++ b/lib/raster3d/headerinfo.c @@ -239,6 +239,8 @@ int Rast3d_get_vertical_unit2(RASTER3D_Map *map) * \brief * * Return the name of the unit of map. Units are defined in gis.h. + * The name is in English, whatever the locale, because it is used in + * machine-readable output and in files. * * Vertical units may have temporal type * @@ -247,7 +249,7 @@ int Rast3d_get_vertical_unit2(RASTER3D_Map *map) */ const char *Rast3d_get_vertical_unit(RASTER3D_Map *map) { - return G_get_units_name(map->vertical_unit, 1, 0); + return G_get_units_name_untranslated(map->vertical_unit, 1, 0); } /*---------------------------------------------------------------------------*/ diff --git a/misc/m.measure/main.c b/misc/m.measure/main.c index 32851e61b84..4db17f1d7c6 100644 --- a/misc/m.measure/main.c +++ b/misc/m.measure/main.c @@ -37,6 +37,7 @@ int main(int argc, char **argv) double length, area, f, sq_f; int i, npoints; const char *units_name, *sq_units_name; + int unit_id; enum OutputFormat format; G_JSON_Value *root_value = NULL; G_JSON_Object *root_object = NULL; @@ -114,14 +115,18 @@ int main(int argc, char **argv) y[i] = atof(coords->answers[2 * i + 1]); } - /* determine units */ - if (G_projection() == PROJECTION_LL && !units->answer) { - units_name = G_get_units_name(U_METERS, 1, 0); - sq_units_name = G_get_units_name(U_METERS, 1, 1); + /* determine units; only the plain output is for people, so only it gets + * translated names */ + unit_id = (G_projection() == PROJECTION_LL && !units->answer) + ? U_METERS + : G_units(units->answer); + if (format == PLAIN) { + units_name = G_get_units_name(unit_id, 1, 0); + sq_units_name = G_get_units_name(unit_id, 1, 1); } else { - units_name = G_get_units_name(G_units(units->answer), 1, 0); - sq_units_name = G_get_units_name(G_units(units->answer), 1, 1); + units_name = G_get_units_name_untranslated(unit_id, 1, 0); + sq_units_name = G_get_units_name_untranslated(unit_id, 1, 1); } f = G_meters_to_units_factor(G_units(units->answer)); sq_f = G_meters_to_units_factor_sq(G_units(units->answer)); From 552328a9a1befc580019c728fe1b7af6a8a59f7c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 13:45:18 +0000 Subject: [PATCH 2/5] grass.gunittest: Run tests with English messages The tests check English messages, so many of them fail when run in a translated locale. Set LANGUAGE=C for the tests run with the gunittest invoker and with pytest, where the test files are not run with the invoker. --- conftest.py | 12 ++++++++++++ python/grass/gunittest/invoker.py | 2 ++ python/grass/gunittest/main.py | 4 ++++ 3 files changed, 18 insertions(+) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000000..a4341c0f383 --- /dev/null +++ b/conftest.py @@ -0,0 +1,12 @@ +"""Settings for all pytest tests""" + +import os + + +def pytest_configure(config): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + os.environ["LANGUAGE"] = "C" diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 7805902c7bd..712400e722c 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -166,6 +166,8 @@ def _run_test_module( env["GISRC"] = gisrc # percentage in plain format is 0...10...20... ...100 env["GRASS_MESSAGE_FORMAT"] = "plain" + # tests check English messages, whatever the locale of the caller + env["LANGUAGE"] = "C" stdout_path = os.path.join(cwd, "stdout.txt") stderr_path = os.path.join(cwd, "stderr.txt") diff --git a/python/grass/gunittest/main.py b/python/grass/gunittest/main.py index f8458ec93d1..83986a88d92 100644 --- a/python/grass/gunittest/main.py +++ b/python/grass/gunittest/main.py @@ -107,6 +107,10 @@ def test(): # pass # TODO: add some message somewhere + # tests check English messages, whatever the locale of the caller; the + # invoker sets this too, but a test file can also be run directly + os.environ["LANGUAGE"] = "C" + # TODO: enable passing omit to exclude also gunittest or nothing program = GrassTestProgram( module="__main__", exit_at_end=False, grass_location="all" From 1973bdeb5eef539d1bc6eceeb12916fbac91d390 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:35:08 +0000 Subject: [PATCH 3/5] libgis: Format units.c with clang-format --- lib/gis/units.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gis/units.c b/lib/gis/units.c index ce1819d4da1..004093c6538 100644 --- a/lib/gis/units.c +++ b/lib/gis/units.c @@ -215,9 +215,11 @@ static const char *units_name(int units, int plural, int square, int localize) if (square) return plural // GTC: localized area units - ? (localize ? _("square kilometers") : "square kilometers") + ? (localize ? _("square kilometers") + : "square kilometers") // GTC: localized area units - : (localize ? _("square kilometer") : "square kilometer"); + : (localize ? _("square kilometer") + : "square kilometer"); else return plural // GTC: localized units From e21e34b007301d11dcbb4707de77e6a8a0724f27 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:55:03 +0000 Subject: [PATCH 4/5] tests: Keep pytest messages in English without a root conftest.py An autotools configure run deletes conftest* files in the source tree, so a root conftest.py cannot hold the setting. Use a session fixture in the conftest.py of each directory with pytest tests instead. --- conftest.py | 12 ------------ general/conftest.py | 15 +++++++++++++++ imagery/conftest.py | 15 +++++++++++++++ lib/conftest.py | 15 +++++++++++++++ python/conftest.py | 15 +++++++++++++++ raster/conftest.py | 12 ++++++++++++ scripts/conftest.py | 12 ++++++++++++ temporal/conftest.py | 15 +++++++++++++++ vector/conftest.py | 12 ++++++++++++ 9 files changed, 111 insertions(+), 12 deletions(-) delete mode 100644 conftest.py create mode 100644 general/conftest.py create mode 100644 imagery/conftest.py create mode 100644 lib/conftest.py create mode 100644 python/conftest.py create mode 100644 temporal/conftest.py diff --git a/conftest.py b/conftest.py deleted file mode 100644 index a4341c0f383..00000000000 --- a/conftest.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Settings for all pytest tests""" - -import os - - -def pytest_configure(config): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - os.environ["LANGUAGE"] = "C" diff --git a/general/conftest.py b/general/conftest.py new file mode 100644 index 00000000000..3f091bed6f1 --- /dev/null +++ b/general/conftest.py @@ -0,0 +1,15 @@ +"""Settings for pytest tests""" + +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/imagery/conftest.py b/imagery/conftest.py new file mode 100644 index 00000000000..3f091bed6f1 --- /dev/null +++ b/imagery/conftest.py @@ -0,0 +1,15 @@ +"""Settings for pytest tests""" + +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/lib/conftest.py b/lib/conftest.py new file mode 100644 index 00000000000..3f091bed6f1 --- /dev/null +++ b/lib/conftest.py @@ -0,0 +1,15 @@ +"""Settings for pytest tests""" + +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/python/conftest.py b/python/conftest.py new file mode 100644 index 00000000000..3f091bed6f1 --- /dev/null +++ b/python/conftest.py @@ -0,0 +1,15 @@ +"""Settings for pytest tests""" + +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/raster/conftest.py b/raster/conftest.py index 88b6d553686..75775677a92 100644 --- a/raster/conftest.py +++ b/raster/conftest.py @@ -44,3 +44,15 @@ def session_tools(session_in_mapset): """ with Tools(session=session_in_mapset) as tools: yield tools + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/scripts/conftest.py b/scripts/conftest.py index d96ac31ee97..556a1349761 100644 --- a/scripts/conftest.py +++ b/scripts/conftest.py @@ -55,3 +55,15 @@ def xy_empty_dataset_session(tmp_path): ): tools.g_region(s=0, n=5, w=0, e=6, res=1) yield session + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/temporal/conftest.py b/temporal/conftest.py new file mode 100644 index 00000000000..3f091bed6f1 --- /dev/null +++ b/temporal/conftest.py @@ -0,0 +1,15 @@ +"""Settings for pytest tests""" + +import pytest + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield diff --git a/vector/conftest.py b/vector/conftest.py index c183dd0be08..a3e572717fa 100644 --- a/vector/conftest.py +++ b/vector/conftest.py @@ -12,3 +12,15 @@ def xy_dataset_session(tmp_path): gs.create_project(tmp_path / "test") with gs.setup.init(tmp_path / "test", env=os.environ.copy()) as session: yield session + + +@pytest.fixture(scope="session", autouse=True) +def english_messages(): + """Keep messages in English for tests which check them + + The tests are written for English messages, so they would fail in a + translated locale, e.g., when running on a system which is not English. + """ + with pytest.MonkeyPatch.context() as monkeypatch: + monkeypatch.setenv("LANGUAGE", "C") + yield From 6930f79c5fb4598c7d682907ec27be683806f09c Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 16:58:35 +0000 Subject: [PATCH 5/5] tests: Set the pytest message language at import of conftest.py Ruff rule RUF076 does not allow autouse fixtures. Set LANGUAGE when the conftest.py files are imported instead, which happens during collection, before any test runs. --- general/conftest.py | 16 ++++------------ imagery/conftest.py | 16 ++++------------ lib/conftest.py | 16 ++++------------ python/conftest.py | 16 ++++------------ raster/conftest.py | 17 +++++------------ scripts/conftest.py | 17 +++++------------ temporal/conftest.py | 16 ++++------------ vector/conftest.py | 17 +++++------------ 8 files changed, 35 insertions(+), 96 deletions(-) diff --git a/general/conftest.py b/general/conftest.py index 3f091bed6f1..8b11707a8a0 100644 --- a/general/conftest.py +++ b/general/conftest.py @@ -1,15 +1,7 @@ """Settings for pytest tests""" -import pytest +import os - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" diff --git a/imagery/conftest.py b/imagery/conftest.py index 3f091bed6f1..8b11707a8a0 100644 --- a/imagery/conftest.py +++ b/imagery/conftest.py @@ -1,15 +1,7 @@ """Settings for pytest tests""" -import pytest +import os - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" diff --git a/lib/conftest.py b/lib/conftest.py index 3f091bed6f1..8b11707a8a0 100644 --- a/lib/conftest.py +++ b/lib/conftest.py @@ -1,15 +1,7 @@ """Settings for pytest tests""" -import pytest +import os - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" diff --git a/python/conftest.py b/python/conftest.py index 3f091bed6f1..8b11707a8a0 100644 --- a/python/conftest.py +++ b/python/conftest.py @@ -1,15 +1,7 @@ """Settings for pytest tests""" -import pytest +import os - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" diff --git a/raster/conftest.py b/raster/conftest.py index 75775677a92..e07a0427945 100644 --- a/raster/conftest.py +++ b/raster/conftest.py @@ -12,6 +12,11 @@ from grass.experimental import TemporaryMapsetSession +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" + + @pytest.fixture(scope="module") def session_with_raster_for_module(tmp_path_factory): """Active session with CRS and raster data""" @@ -44,15 +49,3 @@ def session_tools(session_in_mapset): """ with Tools(session=session_in_mapset) as tools: yield tools - - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield diff --git a/scripts/conftest.py b/scripts/conftest.py index 556a1349761..5e241ee14c7 100644 --- a/scripts/conftest.py +++ b/scripts/conftest.py @@ -14,6 +14,11 @@ from grass.experimental import TemporaryMapsetSession +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" + + @pytest.fixture(scope="module") def monkeypatch_module() -> Generator[pytest.MonkeyPatch]: """Yield a monkeypatch context, through a module-scoped fixture""" @@ -55,15 +60,3 @@ def xy_empty_dataset_session(tmp_path): ): tools.g_region(s=0, n=5, w=0, e=6, res=1) yield session - - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield diff --git a/temporal/conftest.py b/temporal/conftest.py index 3f091bed6f1..8b11707a8a0 100644 --- a/temporal/conftest.py +++ b/temporal/conftest.py @@ -1,15 +1,7 @@ """Settings for pytest tests""" -import pytest +import os - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" diff --git a/vector/conftest.py b/vector/conftest.py index a3e572717fa..1dffba74493 100644 --- a/vector/conftest.py +++ b/vector/conftest.py @@ -6,21 +6,14 @@ import grass.script as gs +# The tests are written for English messages, so they would fail in a +# translated locale, e.g., when running on a system which is not English. +os.environ["LANGUAGE"] = "C" + + @pytest.fixture def xy_dataset_session(tmp_path): """Creates a session with XY project""" gs.create_project(tmp_path / "test") with gs.setup.init(tmp_path / "test", env=os.environ.copy()) as session: yield session - - -@pytest.fixture(scope="session", autouse=True) -def english_messages(): - """Keep messages in English for tests which check them - - The tests are written for English messages, so they would fail in a - translated locale, e.g., when running on a system which is not English. - """ - with pytest.MonkeyPatch.context() as monkeypatch: - monkeypatch.setenv("LANGUAGE", "C") - yield